Django框架基础知识汇总(无项目版)
2018-02-25 19:15
495 查看
一、WEB框架本质
所有的Web应用,本质上其实是一个socket服务端,用户浏览器是一个socket客户端.#!/usr/bin/env python #coding:utf-8 # 服务端SOCKET import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n") client.send("Hello, Seven") def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8000)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()
python web程序分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,在请求到来时对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。
WSGI(Web Server Gateway Interface)是一种规范。它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。
from wsgiref.simple_server import make_server def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print("Serving HTTP on port 8000...") httpd.serve_forever()
二、自定义Web框架
框架:python标准库提供的独立WSGI服务器称为wsgiref。
#通过python标准库提供的wsgiref模块开发一个自己的Web框架 #!/usr/bin/env python #coding:utf-8 from wsgiref.simple_server import make_server def index(): return 'index' def login(): return 'login' def routers(): urlpatterns = ( ('/index/',index), ('/login/',login), ) return urlpatterns def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) url = environ['PATH_INFO'] urlpatterns = routers() func = None for item in urlpatterns: if item[0] == url: func = item[1] break if func: return func() else: return '404 not found' if __name__ == '__main__': httpd = make_server('', 8000, RunServer) print "Serving HTTP on port 8000..." httpd.serve_forever()
模板引擎生成静态内容:
在上叙中,所有login、index均返回给用户浏览器简单字符串,在现实的Web请求中会返回一个复杂的符合HTML规则的字符串,需将要返回给用户的HTML写在指定文件中,然后再返回,如:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <h1>Index</h1> </body> </html> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <form> <input type="text" /> <input type="text" /> <input type="submit" /> </form> </body> </html>
……views模块内容: def index(): # return 'index' f = open('index.html') data = f.read() return data def login(): # return 'login' f = open('login.html') data = f.read() return data ……
开源工具Jinja2实现动态内容:
上述代码返回给用户HTML的内容来现实复杂的页面,但如何给用户返回动态内容?
自定义一套特殊的语法,进行替换
使用开源工具jinja2,遵循其指定语法
# index.html文件内容: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <h1>{{name}}</h1> <ul> {% for item in user_list %} <li>{{item}}</li> {% endfor %} </ul> </body> </html>
#遵循jinja2的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容:
#!/usr/bin/env python # -*- coding:utf-8 -*- from wsgiref.simple_server import make_server from jinja2 import Template def index(): # return 'index' # template = Template('Hello {{ name }}!') # result = template.render(name='John Doe') f = open('index.html') result = f.read() template = Template(result) data = template.render(name='John Doe', user_list=['alex', 'eric']) return data.encode('utf-8') def login(): # return 'login' f = open('login.html') data = f.read() return data def routers(): urlpatterns = ( ('/index/', index), ('/login/', login), ) return urlpatterns def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) url = environ['PATH_INFO'] urlpatterns = routers() func = None for item in urlpatterns: if item[0] == url: func = item[1] break if func: return func() else: return '404 not found' if __name__ == '__main__': httpd = make_server('', 8000, run_server) print "Serving HTTP on port 8000..." httpd.serve_forever()
三、Django基本配置
创建django程序:终端命令:django-admin startproject sitename
IDE创建Django程序时,本质上都是自动执行上述命令
其他常用命令:
python manage.py runserver 0.0.0.0
python manage.py startapp appname # 创建app程序模块
python manage.py syncdb
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
程序目录:
mysite
mysit
init.py
settings.py
urls.py
wsgi.py
templates
manage.py
setting.py 配置文件:
Mysql数据库:
DATABASES = {
‘default’: {
‘ENGINE’: ‘django.db.backends.mysql’,
‘NAME’:’dbname’,
‘USER’: ‘root’,
‘PASSWORD’: ‘xxx’,
‘HOST’: ”,
‘PORT’: ”,
}
}
备注:
由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替
如下设置放置的与project同名的配置的 init.py文件中
**import pymysql
pymysql.install_as_MySQLdb()**
四、路由系统
单一路由对应:url(r’^index$’, views.index)
.
基于正则的路由:
url(r’^index/(\d*)’, views.index), # 匹配0到无穷个数字
url(r’^manage/(?P\w*)/(?P\d*)’, views.manage), # 匹配到两个组,别名分别为name,id
.
添加额外的参数:
url(r’^manage/(?P\w*)’, views.manage,{‘id’:333}), #> 视图函数中的参数可接收id
.
为路由映射设置名称:
url(r’^home’, views.home, name=’h1’),
url(r’^index/(\d*)’, views.index, name=’h2’),
设置名称之后,可以在不同的地方调用,如:
模板中使用生成URL {% url ‘h2’ 2012 %}
函数中使用生成URL reverse(‘h2’, args=(2012,)) 路径:django.urls.reverse
Model中使用获取URL 自定义get_absolute_url() 方法:
class NewType(models.Model): caption = models.CharField(max_length=16) def get_absolute_url(self): """ 为每个对象生成一个URL 应用:在对象列表中生成查看详细的URL,使用此方法即可!!! :return: """ # return '/%s/%s' % (self._meta.db_table, self.id) # 或 from django.urls import reverse return reverse('NewType.Detail', kwargs={'nid': self.id})
根据app对路由规则进行分类:
url(r’^web/’,include(‘web.urls’)),
命名空间: # a. project.urls.py: 1cca8 from django.conf.urls import url,include urlpatterns = [ url(r'^a/', include('app01.urls', namespace='author-polls')), url(r'^b/', include('app01.urls', namespace='publisher-polls')), ] # b. app01.urls.py: from django.conf.urls import url from app01 import views app_name = 'app01' urlpatterns = [ url(r'^(?P<pk>\d+)/$', views.detail, name='detail') ] # c. app01.views.py: def detail(request, pk): print(request.resolver_match) return HttpResponse(pk) # 以上定义带命名空间的url之后,使用name反向生成URL时候,应该如下: v = reverse('app01:detail', kwargs={'pk':11}) {% url 'app01:detail' pk=12 pp=99 %}
django中的路由系统和其他语言的框架有所不同,在django中每一个请求的url都要有一条路由映射,这样才能将请求交给一个views中的函数去处理。其他大部分的Web框架则是对一类的url请求做一条路由映射,从而是路由系统变得简洁。
五、模板
模版的执行:模版的创建过程,是读取模版(其中嵌套着模版标签),然后将 Model 中获取的数据插入到模版中,最后将信息返回给用户.
def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
内置模版语言:
{{ item }} # 获得字典数据 {% for item in item_list %} <a>{{ item }}</a> {% endfor %} # for循环出数据 forloop.counter forloop.first forloop.last {% if ordered_warranty %} {% else %} {% endif %} # if判断数据 母板:{% block title %}{% endblock %} # title为母板名称 子板:{% extends "base.html" %} {% block title %}{% endblock %} # 子板继承母板title 帮助方法: {{ item.event_start|date:"Y-m-d H:i:s"}} # 日期函数应用 {{ bio|truncatewords:"30" }} {{ my_list|first|upper }} # simple_tag实现模板中加载函数执行 {{ name|lower }} # 模版中应用小写的函数
自定义simple_tag模版语言:
定义:
a、在app中创建templatetags模块文件夹 b、创建任意 .py 文件,如:xx.py # xx.py 文件内容如下: #!/usr/bin/env python #coding:utf-8 from django import template from django.utils.safestring import mark_safe register = template.Library() @register.simple_tag def my_simple_time(v1,v2,v3): return v1 + v2 + v3 @register.simple_tag # 将函数注册到simple_tag中 def my_input(id,arg): # 定义处理的函数 result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result) # 创建html能识别的格式标签,而非字符
使用方式:
使用自定义simple_tag的html文件中导入之前创建的 xx.py 文件名 {% load xx %} # 加载自定义的simple_tag 所在的文件名 {% my_simple_time 1 2 3%} # 使用自定义的simple_tag函数名,并传入参数 {% my_input 'id_username' 'hide'%}
注意:
在settings中配置当前app,不然django无法找到自定义的simple_tag: INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', # 注册 )
七、中间件
中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件。
中间件中可以定义5个方法,分别是:
process_request(self,request) # 重要 process_view(self, request, callback, callback_args, callback_kwargs) process_template_response(self,request,response) process_exception(self, request, exception) process_response(self, request, response) # 重要
以上方法的返回值可以是None和HttpResonse对象。
如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户,不再走其他中间件的request方法
引用块内容:
创建中间件类:
class RequestExeute(object): def process_request(self,request): # 先执行所有中间件的process_request函数 pass def process_view(self, request, callback, callback_args, callback_kwargs): i =1 pass def process_exception(self, request, exception): pass def process_response(self, request, response): #所有中间件的所有process_request执行完后再执行process_response函数 return response
注册中间件:
MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'wupeiqi.middleware.auth.RequestExeute', )
八、admin后台
使用步骤:django amdin是django提供的一个后台管理页面,该管理页面提供完善的html和css,
使得在通过Model创建完数据库表后,就可对数据直接进行增删改查。
创建后台管理员:python manage.py createsuperuser
配置url :url(r’^admin/’, include(admin.site.urls)) # 默认会自动生成
注册和配置django admin后台管理页面:
.
# 设置数据表名称: class UserType(models.Model): name = models.CharField(max_length=50) class Meta: # 元信息类 verbose_name = '用户类型' verbose_name_plural = '用户类型'
# 在admin.py模块中执行如下配置: from django.contrib import admin from app01 import models admin.site.register(models.UserType) # 将model模块中的UserType类注册到admin中 admin.site.register(models.UserInfo) admin.site.register(models.UserGroup) admin.site.register(models.Asset)
# 打开表之后,可设定默认显示,需要在model.py模块中作如下配置: class UserType(models.Model): name = models.CharField(max_length=50) def __unicode__(self): return self.name class UserInfoAdmin(admin.ModelAdmin): list_display = ('username', 'password', 'email') # 默认显示
# 为数据表添加搜索功能: from django.contrib import admin from app01 import models class UserInfoAdmin(admin.ModelAdmin): list_display = ('username', 'password', 'email') search_fields = ('username', 'email') # 搜索功能
# 添加快速过滤: from django.contrib import admin from app01 import models class UserInfoAdmin(admin.ModelAdmin): list_display = ('username', 'password', 'email') search_fields = ('username', 'email') list_filter = ('username', 'email') # 快速过滤
八、Model类
使用前准备工作:创建数据库,设计表结构和字段
使用 MySQLdb 来连接数据库,并编写数据访问层代码
业务逻辑层去调用数据访问层执行数据库操作
import MySQLdb def GetList(sql): db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost') # 连接数据库 cursor = db.cursor() # 获得游标 cursor.execute(sql) # 执行sql语句 data = cursor.fetchall() # 获得数据表所有数据 db.close() # 关闭连接 return data def GetSingle(sql): db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost') cursor = db.cursor() cursor.execute(sql) data = cursor.fetchone() db.close() return data
django为使用一种新的方式,即:对象关系映射(Object Relational Mapping,简称ORM)。
django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。
创建数据表:
- 基本结构:
from django.db import models class userinfo(models.Model): name = models.CharField(max_length=30) # 定义字符格式字段 email = models.EmailField() # 邮箱格式字段,会自动进行格式校验 memo = models.TextField() # 文本格式字段
- 字段:
AutoField(Field) # int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) # bigint自增列,必须填入参数 primary_key=True # 注:当model中如果没有自增列,则自动会创建一个列名为id的列 from django.db import models class UserInfo(models.Model): username = models.CharField(max_length=32) # 自动创建一个列名为id的且为自增的整数列在其中 class Group(models.Model): nid = models.AutoField(primary_key=True) # 手动自定义自增列 name = models.CharField(max_length=32) SmallIntegerField(IntegerField): # 小整数 -32768 ~ 32767 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) # 正小整数 0 ~ 32767 IntegerField(Field) # 整数列(有符号的) -2147483648 ~ 2147483647 BigIntegerField(IntegerField): # 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 # 自定义无符号整数字段: class UnsignedIntegerField(models.IntegerField): def db_type(self, connection): return 'integer UNSIGNED' # PS: 返回值为字段在数据库中的属性,Django字段默认的值为: 'AutoField': 'integer AUTO_INCREMENT', 'BigAutoField': 'bigint AUTO_INCREMENT', 'BinaryField': 'longblob', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 'DateField': 'date', 'DateTimeField': 'datetime', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'DurationField': 'bigint', 'FileField': 'varchar(%(max_length)s)', 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', 'IntegerField': 'integer', 'BigIntegerField': 'bigint', 'IPAddressField': 'char(15)', 'GenericIPAddressField': 'char(39)', 'NullBooleanField': 'bool', 'OneToOneField': 'integer', 'PositiveIntegerField': 'integer UNSIGNED', 'PositiveSmallIntegerField': 'smallint UNSIGNED', 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', 'TextField': 'longtext', 'TimeField': 'time', 'UUIDField': 'char(32)', BooleanField(Field) # 布尔值类型 NullBooleanField(Field): # 可以为空的布尔值 CharField(Field) # 字符类型,必须提供max_length参数, max_length表示字符长度 TextField(Field) # 文本类型 EmailField(CharField): # 字符串类型,Django Admin以及ModelForm中提供验证机制 IPAddressField(Field) # 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制 GenericIPAddressField(Field) # 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 # 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要 protocol="both" URLField(CharField) # 字符串类型,Django Admin以及ModelForm中提供验证 URL SlugField(CharField) # 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) CommaSeparatedIntegerField(CharField) # 字符串类型,格式必须为逗号分割的数字 UUIDField(Field) # 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 FilePathField(Field) # 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 # 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 FileField(Field) # 字符串,路径保存在数据库,文件上传到指定目录 # 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage ImageField(FileField) # 字符串,路径保存在数据库,文件上传到指定目录 # 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串) DateTimeField(DateField) # 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field) # 日期格式 YYYY-MM-DD TimeField(DateTimeCheckMixin, Field) # 时间格式 HH:MM[:ss[.uuuuuu]] DurationField(Field) # 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 FloatField(Field) # 浮点型 DecimalField(Field) # 10进制小数 # 参数: max_digits,小数总长度 decimal_places,小数位长度 BinaryField(Field) # 二进制类型
- 参数:
null 数据库中字段是否可以为空 db_column 数据库中字段的列名 db_tablespace default 数据库中字段的默认值 primary_key 数据库中字段是否为主键 db_index 数据库中字段是否可以建立索引 unique 数据库中字段是否可以建立唯一索引 unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引 unique_for_month 数据库中字段【月】部分是否可以建立唯一索引 unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 verbose_name Admin中显示的字段名称 blank Admin中是否允许用户输入为空 editable Admin中是否可以编辑 help_text Admin中该字段的提示信息 choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息; 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date 如:{'null': "不能为空.", 'invalid': '格式错误'} validators 自定义错误验证(列表类型),从而定制想要的验证规则 from django.core.validators import RegexValidator from django.core.validators import EmailValidator,URLValidator,DecimalValidator, MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator 如: test = models.CharField( max_length=32, error_messages={ 'c1': '优先错信息1', 'c2': '优先错信息2', 'c3': '优先错信息3', }, validators=[ RegexValidator(regex='root_\d+', message='错误了', code='c1'), RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'), EmailValidator(message='又错误了', code='c3'), ] )
- 元信息:
class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) class Meta: # 数据库中生成的表名称: app名称 + 下划线 + 类名 (默认生成) db_table = "table_name" # 联合索引 index_together = [ ("pub_date", "deadline"), ] # 联合唯一索引 unique_together = (("driver", "restaurant"),) # admin中显示的表名称 verbose_name # verbose_name加s verbose_name_plural
- 补充知识:
1.触发Model中的验证和错误提示有两种方式: a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息。 b. 调用Model对象的 clean_fields 方法,如: # models.py class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) email = models.EmailField(error_messages={'invalid': '格式错了.'}) # views.py def index(request): obj = models.UserInfo(username='11234', email='uu') try: print(obj.clean_fields()) except Exception as e: print(e) return HttpResponse('ok') # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。 2.Admin中修改错误提示: # admin.py from django.contrib import admin from model_club import models from django import forms class UserInfoForm(forms.ModelForm): username = forms.CharField(error_messages={'required': '用户名不能为空.'}) email = forms.EmailField(error_messages={'invalid': '邮箱格式错误.'}) age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'}) class Meta: model = models.UserInfo # fields = ('username',) fields = "__all__" class UserInfoAdmin(admin.ModelAdmin): form = UserInfoForm admin.site.register(models.UserInfo, UserInfoAdmin) # 注册到admin组件中
创建连表:
一对多:models.ForeignKey(其他表)
多对多:models.ManyToManyField(其他表)
一对一:models.OneToOneField(其他表)
应用场景:
一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。
多对多:在某表中创建一行数据是,有一个可以多选的下拉框
例如:创建用户信息,需要为用户指定多个不同的爱好,生成多条1个用户与不同爱好关联的数据,且一个爱好对应多个用户的数据。
一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了,相当于一对多+唯一性约束)
例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列新的数据
字段以及参数:
ForeignKey
ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要进行关联的表名 to_field=None, # 要关联的表中的字段名称 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 - models.CASCADE,删除关联数据,与之关联也删除 - models.DO_NOTHING,删除关联数据,引发错误IntegrityError - models.PROTECT,删除关联数据,引发错误ProtectedError - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) - models.SET,删除关联数据, a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) def func(): return 10 class MyModel(models.Model): user = models.ForeignKey( to="User", to_field="id" on_delete=models.SET(func),) related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: # 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') db_constraint=True # 是否在数据库中创建外键约束 parent_link=False # 在Admin中是否显示关联数据
OneToOneField:
OneToOneField(ForeignKey) to, # 要进行关联的表名 to_field=None # 要关联的表中的字段名称 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 ###### 对于一对一 ###### # 1. 一对一其实就是 一对多 + 唯一索引 # 2.当两个类之间有继承关系时,默认会创建一个一对一字段 # 如下会在A表中额外增加一个c_part_id列且唯一: class C(models.Model): nid = models.AutoField(primary_key=True) part = models.CharField(max_length=12) class A(C): id = models.AutoField(primary_key=True) code = models.CharField(max_length=1)
ManyToManyField:
ManyToManyField(RelatedField) to, # 要进行关联的表名 related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: # 如: - limit_choices_to={'nid__gt': 5} - limit_choices_to=lambda : {'nid__gt': 5} from django.db.models import Q - limit_choices_to=Q(nid__gt=10) - limit_choices_to=Q(nid=8) | Q(nid__gt=10) symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段 # 做如下操作时,不同的symmetrical会有不同的可选字段 models.BB.objects.filter(...) # 可选字段有:code, id, m1 class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=True) # 可选字段有: bb, code, id, m1 class BB(models.Model): code = models.CharField(max_length=12) m1 = models.ManyToManyField('self',symmetrical=False) through=None, # 自定义第三张表时,使用字段用于指定关系表 through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表 from django.db import models class Person(models.Model): name = models.CharField(max_length=50) class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField( Person, through='Membership', through_fields=('group', 'person'), ) class Membership(models.Model): group = models.ForeignKey(Group, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE) inviter = models.ForeignKey( Person, on_delete=models.CASCADE, related_name="membership_invites", ) invite_reason = models.CharField(max_length=64) db_constraint=True, # 是否在数据库中创建外键约束 db_table=None, # 默认创建第三张表时,数据库中表的名称
单表操作:
基本操作:
增 models.Tb1.objects.create(c1='xx', c2='oo') 增加一条数据,可以接受字典类型数据 **kwargs obj = models.Tb1(c1='xx', c2='oo') obj.save() 查 models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议) models.Tb1.objects.filter().first # 推荐该方式获取单条数据 models.Tb1.objects.all() #获取全部 models.Tb1.objects.filter(name='seven') #获取指定条件的数据 删 models.Tb1.objects.filter(name='seven').delete() 删除指定条件的数据 改 models.Tb1.objects.filter(name='seven').update(gender='0') 将指定条件的数据更新,均支持 **kwargs 第2种修改数据方式: obj = models.Tb1.objects.get(id=1) obj.c1 = '111' # 对c1字段进行赋值班 obj.save() 修改单条数据
进阶操作 (双下划线连表操作)
# 获取个数: 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的值,等价于and #in,范围有效值判断 models.Tb1.objects.filter(id__in=[11, 22, 33]) 获取id等于11、22、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')) #以id为分组条件,分组结果字段别名为c,获取每个组中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) +') # 至少有一个以An或The开头的内容 Entry.objects.get(title__iregex=r'^(an?|the) +') # date日期 Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) # 查询pub_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)
其它高级操作:
# extra (添加额外语句) extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) 例: Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) #F (找到数据表字段) from django.db.models import F models.Tb1.objects.update(num=F('num')+1) # 在所有的num字段基础上加1 # Q (条件组合) 方式一: Q(nid__gt=10) Q(nid=8) | Q(nid__gt=10) Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') 方式二: con = Q() q1 = Q() q1.connector = 'OR' q1.children.append(('id', 1)) q1.children.append(('id', 10)) q1.children.append(('id', 9)) q2 = Q() q2.connector = 'OR' q2.children.append(('c1', 1)) q2.children.append(('c1', 10)) q2.children.append(('c1', 9)) con.add(q1, 'AND') con.add(q2, 'AND') models.Tb1.objects.filter(con) # 等价于((id=1)or(id=10)or(id=9))and((c1=1)or(c1=10)or(c1=9)) # 通过sqlAlchemy执行原生SQL : from django.db import connection, connections cursor = connection.cursor() # 创建游标 cursor = connections['default'].cursor() # 设置游标 cursor.execute("SELECT * from auth_user where id = %s", [1]) # 执行sql语句 row = cursor.fetchone() # 获取一条数据
连表操作:
表结构
class UserProfile(models.Model): user_info = models.OneToOneField('UserInfo') username = models.CharField(max_length=64) password = models.CharField(max_length=64) def __str__(self): return self.username class UserInfo(models.Model): user_type_choice = ( (0, u'普通用户'), (1, u'高级用户'), ) user_type = models.IntegerField(choices=user_type_choice) name = models.CharField(max_length=32) email = models.CharField(max_length=32) address = models.CharField(max_length=128) def __str__(self): return self.name class UserGroup(models.Model): caption = models.CharField(max_length=64) user_info = models.ManyToManyField('UserInfo') def __str__(self): return self.caption class Host(models.Model): hostname = models.CharField(max_length=64) ip = models.GenericIPAddressField() user_group = models.ForeignKey('UserGroup') def __str__(self): return self.hostname
九、扩展内容:(上传文件)
自定义上传文件:def upload_file(request): if request.method == "POST": obj = request.FILES.get('fafafa') # 获得文件对象 f = open(obj.name, 'wb') # 创建文件句柄,以字节形式写入文件 for chunk in obj.chunks(): # 获得文件块 f.write(chunk) # 将文件块写入文件 f.close() return render(request, 'file.html')
Form上传文件实例:
Form定义: class FileForm(forms.Form): ExcelFile = forms.FileField() # 定义表单中的文件上传组件
Model类定义: from django.db import models class UploadFile(models.Model): userid = models.CharField(max_length = 30) file = models.FileField(upload_to = './upload/') date = models.DateTimeField(auto_now_add=True) # 自动添加日期
views: def UploadFile(request): uf = FileForm(request.POST,request.FILES) # 将请求中的数据发送给定义的Form组件进行校验 if uf.is_valid(): # 校验通过,Form组件中包含用户输入到字段对应的值 upload = models.UploadFile() # 实例化Model类,获得对象 upload.userid = 1 # 为对象的userid赋值,可以对应成是为数据表中的字段赋值 upload.file = uf.cleaned_data['ExcelFile'] # 从Form组件中获得ExcelFile中的值传给Model类中对应数据表中的字段 upload.save() # 将该条数据保存到数据库 print (upload.file)
十、Form内置组件
django中的Form一般有2种功能:输入html(生成表单组件)
验证用户输入
Form:
#!/usr/bin/env python # -*- coding:utf-8 -*- import re from django import forms from django.core.exceptions import ValidationError # 定义验证函数: def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') # 定义正则表达式规则,并进行编译 if not mobile_re.match(value): # 判定传入的参数是否匹配正则表达式的规则 raise ValidationError('手机号码格式错误') # 将手机输入框组件的错误信息添加到该字段对应的错误信息列表中 class PublishForm(forms.Form): # 定义Form组件类 user_type_choice = ( (0, u'普通用户'), (1, u'高级用户'), ) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice, attrs={'class': "form-control"})) # 定制下拉框表单组件 title = forms.CharField(max_length=20, min_length=5, error_messages={'required': u'标题不能为空', 'min_length': u'标题最少为5个字符', 'max_length': u'标题最多为20个字符'}, widget=forms.TextInput(attrs={'class': "form-control", # 对组件添加属性 'placeholder': u'标题5-20个字符'})) # 默认提示内容 memo = forms.CharField(required=False, max_length=256, widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'详细描述', 'rows': 3})) phone = forms.CharField(validators=[mobile_validate, ], # 添加验证器,对用户在该表单组件中输入的值进行正则表达式函数的验证 error_messages={'required': u'手机不能为空'}, # 设置验证不通过时前端显示的错误信息 widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'手机号码'})) email = forms.EmailField(required=False, # 该字段非必填项 error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'}, widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
views:
def publish(request): ret = {'status': False, 'data': '', 'error': '', 'summary': ''} # 定义返回参数 if request.method == 'POST': request_form = PublishForm(request.POST) # 将请求数据传入Form组件进行验证 if request_form.is_valid(): request_dict = request_form.clean() # 获得Form组件验证过的数据 ret['status'] = True else: error_msg = request_form.errors.as_json() # 获得错误信息列表的json字符串 ret['error'] = json.loads(error_msg) # 将json字符串转换为字典对象 return HttpResponse(json.dumps(ret)) # 将字典对象转换成json字符串进行传输
扩展ModelForm:
在使用Model和Form时,都需要对字段进行定义并指定类型, 通过ModelForm则可以省去From中字段的定义。 class AdminModelForm(forms.ModelForm): class Meta: model = models.Admin #fields = '__all__' fields = ('username', 'email') widgets = { 'email' : forms.PasswordInput(attrs={'class':"alex"}), }
十一、跨站请求伪造攻击(crsf)
简介:crsf会生成随机字符串发送给前端,前端请求可携带随机字符串来进行识别。
django为用户实现防止跨站请求伪造的功能,
通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。
而对于django中设置防跨站请求伪造功能有分为全局和局部。
- 全局: 在settion配置文件中添加中间件 django.middleware.csrf.CsrfViewMiddleware - 局部: @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。 @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。 注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
应用:
普通Form表单:
veiw中设置返回值: return render_to_response('Account/Login.html',data,context_instance=RequestContext(request)) 或者 return render(request, 'xxx.html', data) # 返回的request请求中包含crsf随机字符串发送给前端 html中设置Token: {% csrf_token %} # 从返回中的request中取出crsf_token随机字符串,再次发送请求时,将该值传递给中间件验证
Ajax实现跨站请求验证:
# html部分: <form method="POST" action="/csrf1.html"> {% csrf_token %} <input id="user" type="text" name="user" /> <input type="submit" value="提交"/> <a onclick="submitForm();">Ajax提交</a> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script>
# js文件部分: <script> function submitForm(){ var token = $.cookie('csrftoken'); # 获得cookies中的csrf随机字符串 var user = $('#user').val(); $.ajax({ url: '/csrf1.html', type: 'POST', headers:{'X-CSRFToken': token}, # 将数据添加到请求头中,让Django去取,硬性规定 data: { "user":user}, success:function(arg){ console.log(arg); } }) } </script>
# views部分 def csrf1(request): if request.method == 'GET': return render(request,'csrf1.html') # request中有cookies,其中包含crsf随机字符串发送给前端 else: return HttpResponse('ok')
十二、Cookie
后台获取请求中Cookie:request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) # 获得加盐后的cookies 参数: 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获取(不是绝对,底层抓包可以获取到也可以被覆盖)
前端获取Cookies:
由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。 <script src='/static/js/jquery.cookie.js'></script> $.cookie("list_pager_num", 30,{ path: '/' });
十三、Session
简介:Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
数据库(默认)
缓存
文件
缓存+数据库
加密cookie
数据库Session:
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
配置 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,默认修改之后才保存(默认)
使用示例:
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的随机字符串 在数据库中是否存在(传入cookies中的key,它对应的value是随机字符串) request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key") # 设置session生命周期 request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。
缓存session:
# 配置 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,默认修改之后才保存
文件Session:
# 配置 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,默认修改之后才保存
缓存+数据库Session:
数据库用于做持久化,缓存用于提高效率: a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 …… b. 使用 同上
加密cookies:
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 …… b. 使用 同上
扩展:Session用户验证(将验证封装成装饰器)
def login(func): def wrap(request, *args, **kwargs): # 如果未登陆,跳转到指定页面 if request.path == '/test/': return redirect('http://www.baidu.com') return func(request, *args, **kwargs) return wrap
相关文章推荐
- python开发学习-day15(前端部分知识、web框架、Django创建项目)
- 什么是“技术不行”和“技术行”——基础扎实,熟悉常用语言与框架,有项目经验和心得,精通1、2门语言和框架,知识有一定的深度和广度
- 【实战\聚焦Python分布式爬虫必学框架Scrapy 打造搜索引擎项目笔记】第3章 爬虫基础知识回顾
- 【基础知识】Android学习笔记--项目框架介绍
- javaScript学习基础知识汇总
- 【SpringMVC框架】springmvc的基础知识
- java 框架基础知识(9)----HTTP报文-->MVC
- java基础知识汇总
- django基础入门(2)-创建项目和应用
- Java基础知识强化之集合框架笔记58:Map集合之LinkedHashMap类的概述
- Html+JS+HTMLdom+Json+JQuery 常用基础知识点汇总
- Java基础知识强化之集合框架笔记62:Map集合之HashMap嵌套HashMap
- java反射基础知识汇总
- Spring 基础知识汇总
- 数据库基础知识汇总
- Yii框架官方教程增补篇5——基础知识:入口脚本
- Spring基础知识汇总
- Java基础知识_框架的概念以及用反射开发框架的原理
- JavaWeb小项目涉及的基础知识01
- 黑马程序员-SQL sever基础知识汇总