Python Django
本章内容:
- django安装
- django实例
- django app 实例
- django HttpRequest对象
- django HttpResponse对象
- django JsonResponse对象
- django QueryDict对象
- Render的前世
- project添加一个目录并且可以识别
- django路由系统URL
- django Views
- django Template
- django simple_tag/filter
- django cookie
- django session
- django链接数据库
- 类的写法
- session 与 cookie的区别
- django流程介绍
- django数据库orm
- django shell 操作/自带方法
- django 分页功能
- django csrf
- django 中间件(待)
- django缓存
- django + uwsgi + nginx 启动
- 小练习
- 应用练习
参考文档
Django 安装
Django的安装会涉及一下多个软件,部署的参考文档会有很多,遇到的报错也会很多,百度、goole 可解决
系统字符集:
#echo $LANG en_us.UTF-8
yum源设置:
mirrors.aliyun.com/help/centos or wget -O /etc/yum.repos.d/CentOs-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo
时区设置:
ntpdate time.windows.com
python 相关包安装:
依赖的包: yum -y install openssl-devel readline-devel gcc gcc-c++ pypi setuptools 包,注意回退一个版本比较稳定
Wget -S https://pypi.python.org/packages/dc/8c/7c9869454bdc53e72fb87ace63eac39336879eef6f2bf96e946edbf03e90/setuptools-33.1.1.zip#md5=7963d41d97b94e450e3f8a217be06ffe unzip setuptools-33.1.1.zip cd setuptools-33.1.1 /usr/local/python27/bin/python setup.py install python-2.7.13 版本
wget -S https://www.python.org/ftp/python/2.7.13/Python-2.7.13.tgz ./configure --prefix=/usr/locl/python27 make && make intall pip 安装 /usr/local/python27/bin/easy_install pip vim /etc/pip.conf [global] trusted-host = pypi.douban.com index-url = http://pypi.douban.com/simple [list] format=colums
virtualenv 环境安装
/usr/local/python27/bin/pip install virtualenv cd data/ /usr/local/python27/bin/virtualenv ./python27env source /data/python27/env/bin/activate pip install ipython pip install mysql-python #deactiveate ,退出虚拟环境
django 安装:
pip install "django>=1.8,<1.9" #安装1.8版本 ipython 中 django.get_version() #查看版本
mysql 安装
5.6版本比较稳定 rpm -ivh https://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm sudo yum -y install mysql mysql-server mysql-devel zlib-devel
django 实例
django-admin startproject opsweb #生成opswed项目
项目目录结构:
运行第一个小实例:
需要提前在settings.py里修改这一行: ALLOWED_HOSTS = ["*"]
python manage.py runserver 0.0.0.0:33333
这里运行的时候遇到一个报错:
ImportError: No module named security
这个是由于我在virtualenv环境下创建的这个项目,执行的时候却退出了,需要进入后再执行。
django app 实例
python manage.py startapp dashboard
生成的目录结构:
目录文件说明:
配置app
把app的名字写入:
opsweb/settings.py
再对url进行配置:
添加到opsweb/urls.py中
可以理解为处理dashboard目录,交给后面的函数来处理
配置视图:
配置dashboard中的url:
梳理下流程:
如:发起url请求:http://59.110.12.72:33333/dashboard/hello/
首先匹配到dashboard 这个app,然后又在python27env/reboot/opsweb/opsweb/urls.py中匹配到了 url(r'^dashboard/', include("dashboard.urls")),然后就会去dashboard中查找urls.py. 在其中发现url(r'hello/$',views.hello), 然后就会执行views视图中的hello函数。
django HttpRequest 对象
视图函数中的request是由Django创建的
def index(request): pass
request属性
HttpRequest.scheme
HttpRequest.body
HttpRequest.path
HttpRequest.path_info
HttpRequest.method
HttpRequest.encoding
HttpRequest.GET
HttpRequest.POST
HttpRequest.META
HttpRequest.user
request方法
HttpRequest.get_host()
HttpRequest.get_full_path()
HttpRequest.build_absoulute_uri(location)
HttpRequest.get_signed_cookie()
HttpRequest.is_secure()
HttpRequest.is_ajax()
django HttpResponse对象
传递一个字符串作为页面的内容到HttpResponse构造函数
from django.http import HttpResponse return HttpResponse('Here is the text of hte web page')
return HttpResponse('Test',content_type="text/plain")
reponse属性
HttpResponse.content
HttpResponse.charset
HttpResponse.status_code
HttpResponse.reason_phrase
reponse方法
HttpResponse.__init__(content=”, content_type=None, status=200, reason=None, charset=None)
如何用呢?待了解
django 中 return HttpResponse('内容'),这个内容只能是string格式的,所以需要 json.dumps(res) 下这个res结果
加入这个res是字典,后端已经序列化了,传递给前端后,需要把这个字符串转化为对象, var obj = JSON.parse(data)
前端对象转换为字符串 :
那为题来了,ajax 请求中只能 return HttpResponse 吗?最好是这样,但可以用 render,不过这个返回的是一个渲染好的html页面,处理是个问题,redirect这个是不能用的!
django JsonResponse对象
支持传递list,dict
return JsonResponse([1,2,3], safe = False)
django QueryDict对象
In [1]: from django.http import QueryDict In [2]: Get = QueryDict('a=1&b=2&c=3')
QueryDict的方法:
QueryDict.get(key, default=None)
QueryDict.setdefault(key, default=None)[source]
QueryDict.update(other_dict) QueryDict.items() QueryDict.values() QueryDict.copy() QueryDict.getlist(key, default=None)
QueryDict.setlist(key, list_)[source]
QueryDict.appendlist(key, item)
QueryDict.setlistdefault(key, default_list=None)
QueryDict.lists() QueryDict.pop(key) QueryDict.popitem() QueryDict.dict() QueryDict.urlencode(safe=None)
Render 的前世
这种方法相比render较不适用,建议使用render,学习一下
与flask相似,需要建立一个templates文件夹,把html文件放到这里,然后再渲染
视图中添加:
urls中添加:
html文件:
后端验证登录
request.GET
request.POST
request.FILES #上传文件
request.getlist() #checkbox 等多选的内容
1 def login(request): 2 print request 3 if request.method == "GET": 4 print 'username from get -->',request.GET.get('username') 5 print 'password from get-->',request.GET.get('password') 6 template = loader.get_template('login.html') 7 context = Context({"title":"reboot 运维平台"}) 8 return HttpResponse(template.render(context)) 9 10 elif request.method == 'POST': 11 print 'username from get -->',request.POST.get('username') 12 print 'password from get-->',request.POST.get('password') 13 username = request.POST.get('username') 14 password = request.POST.get('password') 15 return HttpResponse('login ok!')
<html> <head> <title> {{title}} </title> <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script> </head> <body> <h1>用户登录</h1> <ul> <form action="/dashboard/login/" method="post"> <li><input type="text" name="username" id="username" /></li> <li><input type="text" name="password" id="password" /></li> <li><input type="submit" value="login" id="form_submit" /></li> </form> </ul> <script> $(function(){ $('#form_submit').click(function(){ var username = $('#username').val(); var password = $('#password').val(); $.post("/dashboard/login/",{username:username,password:password},function(res){ alert(res) }) return false; #如果不return false的话,就会按照默认的请求走了 }) }) </script> </body> </html>
project 添加一个目录并且可识别
又名静态文件配置
添加static目录,需要添加setting里面的
STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), )
这样,就可以在templates目录下的html文件中引用这个url了
django 路由系统URL
项目下的urls.py的配置
1、/index/
2、/index/(\d+)
3、/index/(?P<nid>\d+)
正则(?P<year>[0-9]{4})用法
相当于定义了一个year变量,在后边匹配到条件,就把取到值复制给year
4、/index/(?P<nid>\d+) name='root'
reverse()
{% url 'root' 1 %}
5、include
当有N个app时,url的编写都在项目的usls.py下就会有点乱,可以把这个文件转移到各个app下的urls.py下,需要在项目urls.py下做如下操作
from django.conf.urls import url, include
urlpatterns = [
url(r'^cmdb/', include('app01.urls'))
url(r'^monitor/', include('app02.urls'))
]
6、默认值(传递参数)
/index/ {'web':'root'}
def func(request, web):
return ......
例子:
url(r'^index/', views.index, {'name':'root'})
def index(request, name):
print(name)
return HttpResponse('ok')
这时候回多传递后面的字典给后端,而浏览器的格式不变。这个字典的变量可以是动态的,如,动态获取了客户的信息,然后传递给后端。这是后再view中定义的函数,也需要传递这个字典的key,不然会报确实参数的错。
7、name
对URL路由关系进行命名, ==> 以后可以根据此名称生成自己想要的URL
url(r'abcdefg', views.index, name='i1') url(r'bbbbbb/(\d+)/(\d+)', views.index, name='i2') url(r'dddddd/(?P<pid>\d+)/(?P<nid>\d+)/', views.index, name='i3')
def func(request, *args, **kwargs): from django.urls import reverse url1 = reverse('i1') #生成的url abcdefg url2 = reverse('i2', args = (1,2,)) #bbbbbb/1/2/ url3 = reverse('i3' , kwargs = {'pid':1, "nid":9}) #dddddd/1/9
action = {% url "i1" %}
{% url "i3" 1 2 %}
{% url 'i3' pid=1 nid = 9%}
django Views
视图获取用户相关请求信息,以及请求头。
print(tpye(request)) #查看类类型
from django.core.handlers.wsgi import WAGIRequest #可以查看到所有的请求头都是包含在environ 方法中的
print(request.environ)
for k,v in request.environ.items(): #去查看请求头过来的所有信息
def login_view(request): if request.method == "GET": return render(request, "public/login.html") else: username = request.POST.get("username", "") userpass = request.POST.get("password", "") user = authenticate(username=username, password=userpass) ret = {"status":0, "errmsg":""} if user: login(request, user) ret['next_url'] = request.GET.get("next") if request.GET.get("next", None) else "/" else: ret['status'] = 1 ret['errmsg'] = "用户名或密码错误,请联系管理员" return JsonResponse(ret)
django Template
模板继承:有时候许多页面有许多相似之处,为了书写简单就可以用到末班继承,
被继承文件 layer.html ,写好公共的内容, {% block content %} {% endblock %} 这个里面是继承的页面中需要自己书写的东西
继承的文件 a.html {% extends 'layer.hmlt' %} #说明下这个内容是从哪里继承的
{% block content %} 书写自己的代码 {% endblock %} ,当然可以写多块内容
模板导入:别人写好的代码块,咱这个页面上面需要导入,比如淘宝上面的每个产品的块
a.html b.html 这些都是一个个小的产品,这时候需要在主页面导入
{% include “a.html” %}
{% include "b.html" %}
django Simple_tag/filter
Simple_tag写法
1、首先要在app的目录下创建一个templatetags(名字不能变)目录,app需要添加到setting里面就不必多说了
2、穿件一个任意名称的python文件,如xxoo.py
3、xxoo.py 代码
from django import template register = template.Library() #register名字不能变 @register.simplt_tag def zidingyi(a,b): #可以添加参数 return a + b
4、配置html文件
{% load xxoo %} //首先需要在html文件的顶部load这个python文件
{% zidingyi 2 5 %} //然后下面可以引用这个函数了
优点:可以传递多个参数
缺点:不能作为if条件
Filter
用法基本相同
from django import template register = template.Library() #register名字不能变 @register.filter def zidingyi(a,b): #可以添加参数,最多只允许两个参数 return a + b
html文件
{% load xxoo %} //首先需要在html文件的顶部load这个python文件
{% ‘第一个参数’|zidingyi:“第二个参数” %} //注意中间不能有空格
django cookie
cookie:以键对的方式存储在客户端
django设置cookie: rep.set_cookie(key,value,参数)
def login(request): user_info = { 'dachengzi':{'pwd':'123'}, 'kangbazi':{'pwd':'123'} } if request.method == 'GET': print('I am here') return render(request, 'login.html') elif request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') print(username,password) res = user_info.get(username) if not res: return render(request, 'login.html') if res['pwd'] == password: print('I am coming to denglu') res = redirect('/denglu/') #可以这种写法 res.set_cookie('username111',username) return res else: return render(request,'login.html') def denglu(request): v = request.COOKIES.get('username111') #提取登录的username if not v: return redirect('/login/') return render(request, 'denglu.html', {'current_user':v}) #在前端显示登录的user
参数:
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获取(不是绝对,底层抓包可以获取到也可以被覆盖)
例:
current_date = datetime.datetime.utcnow()
current_date = current_date + datetime.timedelta(seconds=5)
response.set_cookie('username111', u, expires=current_data)
httponly=False 这只后只有http能获取到cookie,js获取不到
<select onchange="changePageSize()"> <option value="10">10</option> <option value="30">30</option> <option value="50">50</option> <option value="100">100</option> </select> <script src="jquery.js"></script> <script src="jquery.cookie.js"></script> //cookie是基于juery的,对cookie进行操作 <script> function changePageSize(ths){ var v = $(ths).val() $.cookie('per_page_count', v) } </script>
cookie 加salt
obj = HttpResponse('s')
obj.set_signed_cookie('username', "kangbazi", salt='abcdefg') #加盐加密
request.get_signed_cookie('username', salt='abcdefg') #用盐解密
django session
django实现session功能十分简单,在视图函数中添加如下,
request.session['username'] = username
看似简单,其实django帮我们做了许多事情
1、生成随机字符串
2、写到浏览器的cookie中,(sessionid的形式)
3、保存在服务器端的session中,(保存在了数据库中django_session表中)
4、当然这个服务器端存session的字典中也可以保存一些其他的客户的信息,来更好的识别客户
待session设置完毕后需要
python manage.py makemigrations
python manage.py migrate
来使之生效
django的session的缓存时间默认是2周,可以人为设置
request.session.set_expiry(10s) 设置10s过期
还可以在setting里添加一些默认的设置
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,默认修改之后才保存(默认)
django 链接数据库
setting的配置中
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'wang.....',
'USER':'#####',
'PASSWORD':'123456',
'HOST':'59.110.12.72',
'PORT':3306
}
}
测试链接数据是否成功:
python manage.py syncdb
需要注意的是,Django默认使用的是MySQLdb模块链接MYSQL,python3中需要修改为pymysql,在project 同名文件夹下的__init__文件中添加如下代码即可:
import pymysql
pymysql.install_as_MYSQLdb()
链接数据库创建普通用户:
python manage.py shell
from django.contrib.auth.models import User
user = User.objects.create_user('rock','rock@51reboot.com','123456')
创建管理员用户,命令行模式下:
python manage.py createsuperuser --username=reboot --email=reboot@51reboot.com
修改密码:
python manage.py shell
from django.contrib.auth.models import User
user = User.objects.get(username='rock')
user.set_password('rock')
user.save()
用户登录和退出验证:
from django.contrib.auth import authenticate,login,logout def login_view(request): print request if request.method == "GET": template = loader.get_template('login.html') context = Context({"title":"reboot 运维平台"}) return HttpResponse(template.render(context)) elif request.method == 'POST': print 'username from get -->',request.POST.get('username') print 'password from get-->',request.POST.get('password') username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username,password=password) if user: login(request,user) return HttpResponse('login ok!') else: return HttpResponse('login fail') def logout_view(request): logout(request) return HttpResponse('logout success!!')
类方法的写法
from django.views import view
class Home(view): #继承view方法 ''' 执行get、post方法之前都会先执行dispatch方法 ''' def dispatch(self, request, *args, **kwargs): #代用父类中的dispatch print('before') result = super(Home, self).dispatch(request, *args, **kwargs) print('after') def get(self, request): print(request.method) return render(request, 'home.html') def post(self, request): print(request.method, 'POST') return render(request, 'home.html')
session 与 cookie的区别
2,session 默认被存在在服务器的一个文件里(不是内存)
3,session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式 实现,比如在 url 中传递 session_id)
4,session 可以放在 文件、数据库、或内存中都可以。
5,用户验证这种场合一般会用 session
django流程介绍
两种架构:
MVC
即将程序分为三个组成部分, model(模型)、view(视图)、controller(控制器)
M 管理应用程序的状态,
C 接受外部用户的操作,根据操作访问模型获取数据
V 负责把数据格式化后呈献给用户
django 也是MVC的框架,但是在Django中,控制器接受用户输入的部分由框架自行处理,所以Django里更关注的是模型(Model),模块(Template)和视图(Views),成为MTV模式:
M 代表模型(Model) ,即数据存取层,该层处理与数据相关的所有事物:如何存取、如何验证有效性、包含哪些行为以及数据之间的关系等。
T 代表模板(Template), 即表现层,该层处理与表现相关的决定:如何在页面或者其他类型文档中进行显示
V 代表视图(View), 即业务逻辑层,盖层包含存取模型及调取恰当模块的相关逻辑,你可以把它看做模型与模块之间的桥梁。
Djiango 数据库 ORM
小例,通过django orm 添加表,然后admin展示:
首先在dashboard这个app下的models.py添加表
from django.db import models # Create your models here. class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __unicode__(self): #这两行是为了在后台中以名字展现,不然不会显示创建的名称 return "<%s>"%(self.name) class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() def __unicode__(self): return "<%s,%s>"%(self.first_name,self.last_name) class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __unicode__(self): return "<%s>"%(self.title)
表格创建完毕后需要在数据苦衷生成了
python manage.py makemigrations
python manage.py migrate
这样数据库中就会生成对应的表:
表创建完毕了,就需要在app下的admin.py中导入表的信息
from django.contrib import admin # Register your models here. import models admin.site.register(models.Author) admin.site.register(models.Book) admin.site.register(models.Publisher)
python manay.py createsuperuser ,创建可登录admin后台的超级管理员用户
这样在访问http://59.110.12.72:33333/admin/ ,就可以在里面做一些对标的增、删、改、查 了
Django 数据库支持的字段类型:
详见:https://docs.djangoproject.com/en/1.9/ref/models/fields/
Django 中如果想要表格可以输入为空:
null = True 控制的是数据库可以为空
blank = True 告诉Django表格可以为空
然后再发布配置:
使标题显示中文
ORM对数据库进行操作
表中插入数据
python manage.py shell
>>> from dashboard.models import Publisher
>>> p1 = Publisher(name='Apress',address='USA',city='MHD',state_province='..',country='USA',website='www.baidu.com')
>>> p1.save() #这种方法,需要save()后才会添加到数据库
#下面这种方法,可以直接在添加到数据库
>>> p1 = Publisher.objects.create(name='laoboy',address='USA',city='MHD',state_province='..',country='USA',website='www.baidu.com')
这套命令对应这数据库操作的 insert
如果要改变其中某个字段的值,需要怎么操作呢?
p1.name = 'balabala'
p1.save()
这里保存的时候,是又重新把整个表保存了一遍。
查找对象
>>> Publisher.objects.all()
[<Publisher: <快网出版社>>, <Publisher: <Apress>>, <Publisher: <oldboy>>]
注意到Django在选择所有数据时并没有使用 SELECT* ,而是显式列出了所有字段。 设计的时候就是这样:SELECT* 会更慢
>>> Publisher.objects.filter(name='oldboy')
[<Publisher: <oldboy>>]
同样,也支持,多条件过滤
>>> Publisher.objects.filter(name='oldboy',country='USA')
[<Publisher: <oldboy>>]
模糊查询
>>> Publisher.objects.filter(name__contains="boy") #中间是双横杠
[<Publisher: <oldboy>>]
获取单个对象并对齐修改
>>> Publisher.objects.get(name='oldboy')
<Publisher: <oldboy>>
>>> a = Publisher.objects.get(name='oldboy')
>>> a.name = 'oldboy2'
>>> a.save()
获取对象后进行排序
>>> Publisher.objects.order_by(
"name"
) #按照name来排序
>>> Publisher.objects.order_by(
"state_province"
,
"address"
) #第一项一样的,按照第二项来排序
>>> Publisher.objects.order_by(
"-name"
) #倒叙
排来排去有点麻烦,可以用meta来进行全局的排序
连锁查询
>>> Publisher.objects.
filter
(country
=
"U.S.A."
).order_by(
"-name"
)
[<Publisher: O'Reilly>, <Publisher: Apress>]
>>> Publisher.objects.order_by(
'name'
)[
0
] #指向要帅选出来的第一条数据
<Publisher: Apress>
筛选到数据并修改:
删除对象
django shell 操作/自带方法
shell 中修改密码:
from django.contrib.auth.models import User u = User.objects.get(username='admin') #得到admin用户的对象 u.set_password('123') #设置密码 u.password #查看的密码就是加密后的 #添加新用户 u2 = User() u2.username = 'u2' u2.set_password('123') u2.save() #设置is_staff后便可以登录后台系统了 u2.is_staff = True #django user登录鉴权 from django.contrib.auth import authenticate test = authenticate(username='u2', password='123') #如果有值则存在,None则不存在
用户验证登录的时候如果没有验证成功,django会有一个自己的rewritelogin的地址如果想要更改这个login的得知的话,则需要在settings配置文件中添加如下一行
LOGIN_URL = ‘/login’
django自带方法
1、login
from django.contrib.auth import login, authenticate user = authenticate(username='u2', password='123') login(request, user) #登录
2、login_required
from django.contrib.auth.decorators import login_required @login_required def index(request): #装饰到函数前面 pass
django分页功能
xss: django为了安全机制,默认传递的都是字符串,比如来自浏览客户的评论,如果给写了个js脚本,抱歉那是不会执行的,只是按照字符串来对待
确定安全的情况下,那如何让其对这字符串做解释呢?
方法:
1、前端 {{ page_str |safe}} //对后端传递过来的字符串作解释
2、后端 from django.utils.safestring import mark_safe
page_str = mark_safe(page) #对html的字符串做mark_safe后,再传递
手写一个分页:
def user_list(request): current_page = request.GET.get('p',1) #获取参数p,没有就默认为1 start = (current_page-1) * 10 end = current_page * 10 data = LIST[start:end] #LIST 想象为数据库获取的数据 all_count = len(LIST) count, yushu = divmod(all_count, 10) if y: count += 1 page_list = [] for i in range(1, count+1): temp = '<a href="/user_list/?p=%s>%s</a>"' %(i,i) page_list.append(temp) page_str = "".join(page_list) page_str = mark_safe(page_str) return render(request, 'user_list.html' , {'li': data, 'page_str': page_str})
django csrf
CSRF, Cross Site Request Forgery, 跨站点伪造请求。举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造的。具体的细节及其危害见 wikipedia
机制:django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,这样就能避免被 CSRF 攻击。
setting中开启csrf功能后,在没有做特殊处理的情况下会遇到下面的报错:
那如何解决这个问题呢?
1、form 表单提交的的时候需要添加上{% csrf_token %}
<form action="/cref/" method="post"> {% csrf_token %} <div> 用户名:<input type="text" name="username"> </div> <div> 密码:<input type="text" name="password"> </div> <div> <input type="submit" value="提交"> </div> </form>
2、如果是ajax请求呢?那就需要在请求的时候,在header中添加这么一行,headers:{'X-CSRFtoken':csrf_token},
<script> $(function () { $('#btn').click(function () { var csrf_token = $.cookie('csrftoken') $.ajax({ url:'/cref/', type:"POST", headers:{'X-CSRFtoken':csrf_token}, data : {'username':'admin','password':'123'}, success:function(){ } }) }) }) </script>
如果你的ajax很多,老是添加这么一行,那岂不是很麻烦呢?那只需要在开头添加一个ajaxSetup即可
$(function () {
$.ajaxSetup({
beforeSend:function (xhr,settings) {
xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken'))
}
});
$('#btn').click(function () {
var csrf_token = $.cookie('csrftoken')
$.ajax({
url:'/cref/',
type:"POST",
//headers:{'X-CSRFtoken':csrf_token},
data : {'username':'admin','password':'123'},
success:function(){
}
})
})
})
另外想一个问题,如果有100个views,只有其中 两个需要加这个需求,另外的都不加,再如果有两个加,其他的都不加呢?这里就涉及到全局和局部使之生效的问题
全局:
中间件 django.middleware.csrfViewMiddleware
局部:
- @csrf_protect, 为当前的幻术强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件
django + uwsgi + nginx 启动
使用runserver可以使我们的django项目很便捷的在本地运行起来,但这只能在局域网内访问,如果在生产环境部署django,就要多考虑一些问题了。比如静态文件处理,安全,效率等等,本篇文章总结归纳了一下基于uwsgi+Nginx下django项目生产环境的部署
准备知识:
Django:一个基于python的开源web框架
uwsgi:一个基于自由uwagi协议、wsgi协议和http服务协议的web网关
nginx:常用的高性能代理服务器
wsgi.py : django项目携带的一个wsgi接口文件
项目流程
1、首先客户端请求服务资源,
2、nginx作为直接对外的服务接口,接收到客户端发送过来的http请求,会解包、分析,
3、如果是静态文件请求就根据nginx配置的静态文件目录,返回请求的资源,
4、如果是动态的请求,nginx就通过配置文件,将请求传递给uWSGI;uWSGI 将接收到的包进行处理,并转发给wsgi,
5、wsgi根据请求调用django工程的某个文件或函数,处理完后django将返回值交给wsgi,
6、wsgi将返回值进行打包,转发给uWSGI, uWSGI接收后转发给nginx,nginx最终将返回值返回给客户端(如浏览器)。 *
注:不同的组件之间传递信息涉及到数据格式和协议的转换
软件安装:
1.yum -y install gcc openssl openssl-devel
2.install python3
./configure --prefix=/usr/local --with-ssl
make && make install
3. istall pip3
url:https://github.com/pypa/pip/archive/1.5.5.tar.gz
python3 setup.py install
/usr/local/python3/bin pip 安装的命令都放到这个目录下
4.pip3 install uwsgi
5. install nginx
tar 包安装的ngin,需要自己添加快速启动脚本到 /etc/init.d/下
#!/bin/sh # # nginx - this script starts and stops the nginx daemon # # chkconfig: - 85 15 # description: Nginx is an HTTP(S) server, HTTP(S) reverse \ # proxy and IMAP/POP3 proxy server # processname: nginx # config: /etc/nginx/nginx.conf # config: /etc/sysconfig/nginx # pidfile: /var/run/nginx.pid # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ "$NETWORKING" = "no" ] && exit 0 nginx="/usr/local/nginx/sbin/nginx" prog=$(basename $nginx) NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf" [ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx lockfile=/var/lock/subsys/nginx start() { [ -x $nginx ] || exit 5 [ -f $NGINX_CONF_FILE ] || exit 6 echo -n $"Starting $prog: " daemon $nginx -c $NGINX_CONF_FILE retval=$? echo [ $retval -eq 0 ] && touch $lockfile return $retval } stop() { echo -n $"Stopping $prog: " killproc $prog -QUIT retval=$? echo [ $retval -eq 0 ] && rm -f $lockfile return $retval killall -9 nginx } restart() { configtest || return $? stop sleep 1 start } reload() { configtest || return $? echo -n $"Reloading $prog: " killproc $nginx -HUP RETVAL=$? echo } force_reload() { restart } configtest() { $nginx -t -c $NGINX_CONF_FILE } rh_status() { status $prog } rh_status_q() { rh_status >/dev/null 2>&1 } case "$1" in start) rh_status_q && exit 0 $1 ;; stop) rh_status_q || exit 0 $1 ;; restart|configtest) $1 ;; reload) rh_status_q || exit 7 $1 ;; force-reload) force_reload ;; status) rh_status ;; condrestart|try-restart) rh_status_q || exit 0 ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}" exit 2 esac
下面配置uwsgi.ini 文件
# hello_uwsgi.ini file [uwsgi] # Django-related settings socket = :9999 #这里需要配置的socket,如果是http的话,会有错误 #http = :9999 # the base directory (full path) chdir = /home/right/cache_check # Django s wsgi file module = cache_check.wsgi # process-related settings # master master = true # maximum number of worker processes processes = 4 #logto = /var/log/uwsgi/%n.log daemonize = /var/log/uwsgi/dango_uwsgi.log # ... with appropriate permissions - may be needed # chmod-socket = 664 # clear environment on exit vacuum = true plugin = python #harakiri = 12
添加uwsgi快速启动脚本
#!/bin/sh NAME="dango" if [ ! -n "$NAME" ];then echo "no arguments" exit; fi echo $NAME ID=`ps -ef | grep "$NAME" | grep -v "$0" | grep -v "grep" | awk '{print $2}'` echo $ID echo "################################################" for id in $ID do kill -9 $id echo "kill $id" done echo "################################################" uwsgi --ini /home/right/cache_check/dango_uwsgi.ini
添加nginx的配置
location / { include uwsgi_params; uwsgi_pass 127.0.0.1:9999; uwsgi_send_timeout 1600; # 指定连接到后端uWSGI的超时时间。 uwsgi_connect_timeout 1600; # 指定向uWSGI传送请求的超时时间,完成握手后向uWSGI传送请求的超时时间。 uwsgi_read_timeout 1600; # 指定接收uWSGI应答的超时时间,完成握手后接收uWSGI应答的超时时间。 #root html; #index index.html index.htm; #uwsgi_param SCRIPT_NAME; }
到这里配置完毕,可以运行了
针对以上的内容做一个小练习:
可以添加新书,并显示
1 from django.db import models 2 3 # Create your models here. 4 5 class Publisher(models.Model): 6 name = models.CharField(max_length=30) 7 address = models.CharField(max_length=50) 8 city = models.CharField(max_length=60) 9 state_province = models.CharField(max_length=30) 10 country = models.CharField(max_length=50,null=True) 11 website = models.URLField() 12 def __unicode__(self): 13 return "<%s>"%(self.name) 14 15 class Author(models.Model): 16 first_name = models.CharField(max_length=30) 17 last_name = models.CharField(max_length=40) 18 email = models.EmailField() 19 def __unicode__(self): 20 return "<%s,%s>"%(self.first_name,self.last_name) 21 22 class Book(models.Model): 23 title = models.CharField(max_length=100) 24 authors = models.ManyToManyField(Author) 25 publisher = models.ForeignKey(Publisher) 26 publication_date = models.DateField() 27 def __unicode__(self): 28 return "<%s>"%(self.title)
#!/usr/bin/python #coding:utf-8 from django.shortcuts import render from django.http import HttpResponse from django.template import Context,loader from django.contrib.auth import authenticate,login,logout import models # Create your views here. def booklist(request): if request.method == 'POST': book_name = request.POST.get('name') publisher_id = request.POST.get('publisher_id') author_ids = request.POST.get('author_ids') print 'books`s name from post -->',request.POST.get('name') print 'publisher_id from post-->',request.POST.get('publisher_id') print 'author_ids from post-->',request.POST.get('author_ids') new_book = models.Book( title = book_name, publisher_id = publisher_id, publication_date = '2016-05-22' ) new_book.save() new_book.authors.add(*author_ids) #表中多对多,需要另外插入 books = models.Book.objects.all() publisherlist = models.Publisher.objects.all() authorlist = models.Author.objects.all() return render(request, 'books.html',{'books':books,'publisher':publisherlist,'authorlist':authorlist} )
1 <!DOCTYPE html> 2 <html lang="en" xmlns="http://www.w3.org/1999/html"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>booklist</title> 6 <style books_name></style> 7 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" > 8 <style> 9 .book_form{margin-top:10px;} 10 </style> 11 </head> 12 <body> 13 <div class="container"> 14 </br> 15 <ul> 16 {% for book in books %} 17 <li>{{book.title}}</li> 18 19 {% endfor%} 20 </ul> 21 22 </br> 23 <form method='post' action='/dashboard/books/'> 24 <div class="col-xs-4"> 25 输入书名<input type="text" class=" input-sm form-control" name='name' > 26 </div> 27 </br> 28 <div class="col-xs-2"> 29 <select class="input-sm form-control" name="publisher_id"> 30 <option >请选择出版社</option> 31 {% for banshe in publisher%} 32 <option value='{{banshe.id}}'>{{banshe.name}}</option> 33 {% endfor%} 34 35 </select> 36 37 </div> 38 <div class="col-xs-4"> 39 <select multiple class="form-control" name='author_ids'> 40 {% for auth in authorlist%} 41 <option value='{{auth.id}}'>{{auth.first_name}}</option> 42 {% endfor %} 43 </select> 44 </div> 45 46 <input type="submit" value="创建新书"> 47 </form> 48 </div> 49 </body> 50 </html>
应用练习
总结一
用户登录,前端用户ajax请求登录,后端验证,翻过json给前端,前端通过状态码来判断做什么操作
涉及的内容:django login, authenticate, login_required , ajax请求, from表单
from django.http import HttpResponse, JsonResponse from django.http.response import JsonResponse from django.shortcuts import render, redirect from django.contrib.auth import authenticate, login from django.contrib.auth.decorators import login_required @login_required def index(request): return render(request, 'index.html') def mylogin(request): user_info = { 'dachengzi':{'pwd':'123'}, 'kangbazi':{'pwd':'123'} } if request.method == 'GET': print('I am here') return render(request, 'pages/examples/login.html') elif request.method == 'POST': print('I am post request!') username = request.POST.get('username') password = request.POST.get('password') print(username,password) res = authenticate(username=username, password=password) res_json = {} if res is not None: login(request,res) res_json['status'] = 0 else: res_json['status'] = 1 return JsonResponse(res_json)
<form action="#" > //防止from自动get请求,这里填入# <div class="form-group has-feedback"> <input id="username" class="form-control" placeholder="Email"> <span class="glyphicon glyphicon-envelope form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input id="password" type="password" class="form-control" placeholder="Password"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="row"> <div class="col-xs-8"> <div class="checkbox icheck"> <label> <input type="checkbox"> Remember Me </label> </div> </div> <!-- /.col --> <div class="col-xs-4"> <button id="submit" type="submit" class="btn btn-primary btn-block btn-flat">Sign In</button> </div> <!-- /.col --> </div> </form> <script> $('#submit').click(function () { username = $('#username').val() password = $('#password').val() $.ajax({ type:"POST", url:"/login/", data:{username:username, password:password}, dataTpye:"json", success:function (data1) { status = data1.status if(status ==0){ location.href='/' }else{ console.log('error') } } }) }) </script>