# django9
## 文件上传:
文件上传是网站开发中非常常见的功能。这里详细讲述如何在`Django`中实现文件的上传功能。
## 前端HTML代码实现:
1. 在前端中,我们需要填入一个`form`标签,然后在这个`form`标签中指定`enctype="multipart/form-data"`,不然就不能上传文件。
2. 在`form`标签中添加一个`input`标签,然后指定`input`标签的`name`,以及`type="file"`。
以上两步的示例代码如下:
```html
## 后端的代码实现:
后端的主要工作是接收文件。然后存储文件。接收文件的方式跟接收`POST`的方式是一样的,只不过是通过`FILES`来实现。示例代码如下:
```python
def save_file(file):
with open('somefile.txt','wb') as fp:
for chunk in file.chunks():
fp.write(chunk)
def index(request):
if request.method == 'GET':
form = MyForm()
return render(request,'index.html',{'form':form})
else:
myfile = request.FILES.get('myfile')
save_file(myfile)
return HttpResponse('success')
```
以上代码通过`request.FILES`接收到文件后,再写入到指定的地方。这样就可以完成一个文件的上传功能了。
## 使用模型来处理上传的文件:
在定义模型的时候,我们可以给存储文件的字段指定为`FileField`,这个`Field`可以传递一个`upload_to`参数,用来指定上传上来的文件保存到哪里。比如我们让他保存到项目的`files`文件夹下,那么示例代码如下:
## 文件初步上传代码实例
```python
这里只需app下的views.py models.py 和 index.html即可
# models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# thumbnail = models.FileField(upload_to='files')
thumbnail = models.FileField() #指定MEDIA_ROOT和MEDIA_URL即可
# thumbnail = models.FileField(upload_to='%Y/%m/%d')
# views.py
from django.shortcuts import render
from .models import Article
from django.http import HttpResponse
#普通上传文件方法 不能限制文件扩展名
def index(request):
if request.method == "GET":
return render(request,'index.html')
else:
title = request.POST.get('title')
content = request.POST.get('content')
thumbnail = request.FILES.get('thumbnail')
article = Article(title=title,content=content,thumbnail=thumbnail)
article.save()
return HttpResponse("添加成功")
index.html
调用完`article.save()`方法,就会把文件保存到`files`下面,并且会将这个文件的路径存储到数据库中。
## 指定`MEDIA_ROOT`和`MEDIA_URL`:在settings.py最后一行添加即可
以上我们是使用了`upload_to`来指定上传的文件的目录。我们也可以指定`MEDIA_ROOT`,就不需要在`FielField`中指定`upload_to`,他会自动的将文件上传到`MEDIA_ROOT`的目录下。
```python
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
models.py
thumbnail = models.FileField()#如果指定了 MEDIA_ROOT MEDIA_URL 里边uploads可以省略,同时设置是会在media目录下再建一个你指定的目录 将文件放在里面
#没有指定
thumbnail = models.FileField(upload_to="files") #这里边写成 thumbnail
```
然后我们可以在`urls.py`中添加`MEDIA_ROOT`目录下的访问路径。示例代码如下:
http://127.0.0.1:9000/media/day7%E4%BD%9C%E4%B8%9A.txt
从数据库中读取文件的url 然后在页面上能访问 访问: media/数据库路径名
```python
#settings.py中 写入
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
然后在 urls.py中 映射 的
from django.urls import path
from front import views
from django.conf.urls.static import static #这个引入进来
from django.conf import settings #把 settings.py 配置文件引入进来
urlpatterns = [
path('', views.index),
] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
```
如果我们同时指定`MEDIA_ROOT`和`upload_to`,那么会将文件上传到`MEDIA_ROOT`下的`upload_to`文件夹中。示例代码如下:
```python
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
thumbnail = models.FileField(upload_to="%Y/%m/%d/") #指定 上传目录到详细的 目录下
#2018/09/20/boolean默认值.png
```
## 限制上传的文件拓展名:
如果想要限制上传的文件的拓展名,那么我们就需要用到表单来进行限制。我们可以使用普通的`Form`表单,也可以使用`ModelForm`,直接从模型中读取字段。示例代码如下:
```python
# models.py
from django.core import validators
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
thumbnial = models.FileField(upload_to='%Y/%m/%d/',validators=[validators.FileExtensionValidator(['txt','pdf'],message="thumbnial必须为txt或者pdf格式的文件 ")])
# forms.py
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = "__all__"
```
## 上传图片:
上传图片跟上传普通文件是一样的。只不过是上传图片的时候`Django`会判断上传的文件是否是图片的格式(除了判断后缀名,还会判断是否是可用的图片)。如果不是,那么就会验证失败。我们首先先来定义一个包含`ImageField`的模型。示例代码如下:
```python
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
thumbnail = models.ImageField(upload_to="%Y/%m/%d/")
```
## 文件扩展名验证示例代码
因为要验证是否是合格的图片,因此我们还需要用一个表单来进行验证。表单我们直接就使用`ModelForm`就可以了。示例代码如下:
```python
models.py
from django.db import models
from django.core import validators
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
thumbnail = models.FileField(upload_to='%Y/%m/%d',validators=[validators.FileExtensionValidator(['txt','pdf'],message="thumbnail格式必须是txt或者pdf")])
forms.py
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta: #必须叫 Meta
#已模型中的字段为准
model = Article
# __all__ 表示所有的字段都要验证
fields = '__all__'
# fields= "title,content" # 验证指定字段 ,隔开
#错误信息提示
error_messages = {
"thumbnail":{
"invalid_extension":"文件格式必须是txt或者pdf"
}
}
views.py
from django.shortcuts import render
from .forms import ArticleForm
from django.http import HttpResponse
from django.views.generic import View
class IndexView(View):
def get(self,request):
return render(request,'index.html')
def post(self,request):
form = ArticleForm(request.POST,request.FILES)
# 判断是否验证成功
if form.is_valid():
# 成功以后进行保存
form.save()
return HttpResponse("成功")
else:
# 如果错误输出 错误信息
print(form.errors.get_json_data())
return HttpResponse("失败")
----------------上面是验证上传文件的代码 下面是验证上传图片的代码----------------
代码基本同上 只有2出不同
froms.py
class ArticleForm(forms.ModelForm):
class Meta: #必须叫 Meta
model = Article #以模型中的字段为准
fields= "__all__" ##__all__ 表示所有的字段都要验证
#fields= "title,content" ## 指定字段 ,隔开
error_messages = { #错误信息
"thumbnail":{
'invalid_image':'请上传正确格式的图片' #1.只有这里不同
}
}
models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
thumbnail = models.ImageField(upload_to="%Y/%m/%d") #2.这里也不同
```
**注意:使用ImageField,必须要先安装Pillow库:pip install pillow**
# cookie和session
1. cookie:在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。`cookie`的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的`cookie`数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。`cookie`存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用`cookie`只能存储一些小量的数据。
2. session: session和cookie的作用有点类似,都是为了存储用户相关的信息。不同的是,`cookie`是存储在本地浏览器,`session`是一个思路、一个概念、一个服务器存储授权信息的解决方案,不同的服务器,不同的框架,不同的语言有不同的实现。虽然实现不一样,但是他们的目的都是服务器为了方便存储数据的。`session`的出现,是为了解决`cookie`存储数据不安全的问题的。
3. cookie和session使用:`web`开发发展至今,`cookie`和`session`的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里,一般有两种存储方式:
- 存储在服务端:通过`cookie`存储一个`sessionid`,然后具体的数据则是保存在`session`中。如果用户已经登录,则服务器会在`cookie`中保存一个`sessionid`,下次再次请求的时候,会把该`sessionid`携带上来,服务器根据`sessionid`在`session`库中获取用户的`session`数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做`server side session`。`Django`把`session`信息默认存储到数据库中,当然也可以存储到其他地方,比如缓存中,文件系统中等。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器已经发展至今,一些`session`信息还是绰绰有余的。
- 将`session`数据加密,然后存储在`cookie`中。这种专业术语叫做`client side session`。`flask`框架默认采用的就是这种方式,但是也可以替换成其他形式。
## 在django中操作cookie和session:
### 操作cookie:
### cookie 示例代码 和session的赋值
~~~
不用建app 主项目中新建views.py
views.py
from django.http import HttpResponse
from datetime import datetime
from django.utils.timezone import make_aware
def index(request):
response = HttpResponse('index')
expire_time = datetime(year=2018,month=9,day=20,hour=21,minute=30,second=0)
#讲时间转化成格林尼治时间 避免时间差
expire_time = make_aware(expire_time)
#设置cookie键值对及过期时间
response.set_cookie("user_id","bailele",expires=expire_time,path="/cms/")
return response
def cms_view(request):
cookie = request.COOKIES
#遍历上面设置的cookie值
for cookie_key,cookie_value in cookie.items():
#打印cookie的值
print(cookie_key,cookie_value)
return HttpResponse("OK")
def delete_cookie(request):
response = HttpResponse("删除cookie")
#删除cookie
response.delete_cookie("user_id")
return response
def test_view(request):
cookie = request.COOKIES
userid = cookie.get("user_id")
print(userid)
return HttpResponse(userid)
def session_view(request):
#设置session值
request.session['user'] = "python666"
request.session["id"] = "666"
#获取session的值 并输出session值
# user = request.session.pop("user")
# id = request.session.pop("id")
# return HttpResponse("用户名:%s,用户id:%s"%(user,id))
#清除当前这个用户的session数据。
# request.session.clear()
#删除session并且删除在浏览器中存储的session_id,
# 一般在注销的时候用得比较多。
request.session.flush()
user = request.session.get('user')
# return HttpResponse("用户名:%s"%user)#结果为None
# 给session设置过期时间
expire = datetime(year=2018,month=9,day=21,hour=10,minute=30,second=0)
expires = make_aware(expire)
request.session.set_expiry(expires)
return HttpResponse("设置session")
urls.py
from . import views
urlpatterns = [
path('', views.index),
path('cms/', views.cms_view),
path('delete/', views.delete_cookie),
path('test/', views.test_view),
path('session/', views.session_view),
]
~~~
#### 设置cookie:
设置`cookie`是设置值给浏览器的。因此我们需要通过`response`的对象来设置,设置`cookie`可以通过`response.set_cookie`来设置,这个方法的相关参数如下:
```
def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
domain=None, secure=False, httponly=False):
```
1. `key`:这个`cookie`的`key`。
2. `value`:这个`cookie`的`value`。
3. `max_age`:最长的生命周期。单位是秒。
4. `expires`:过期时间。跟`max_age`是类似的,只不过这个参数需要传递一个具体的日期,比如`datetime`或者是符合日期格式的字符串。如果同时设置了`expires`和`max_age`,那么将会使用`expires`的值作为过期时间。默认是 navie时间 不知道位于哪个时区 可以将其转为aware时间
```
from django.utils.timezone import make_aware
expire_time = datetime(year=2018,month=9,day=25,hour=20,minute=30,second=0)
expire_time1 = make_aware(expire_time)
```
1.
> 如果`max_age` 和 `expires`同时存在 以 expires为准
user_id=kangbazi1806; expires=Tue, 25-Sep-2018 20:30:00 GMT; Max-Age=480381; Path=/cms/
2. `path`:对域名下哪个路径有效。默认是对域名下所有路径都有效。 上面 只对 /cms/路径有效
3. `domain`:针对哪个域名有效。默认是针对主域名下都有效,如果只要针对某个子域名才有效,那么可以设置这个属性.
4. `secure`:是否是安全的,如果设置为`True`,那么只能在`https`协议下才可用。默认 `secure` 为false 也就是 http协议能用
5. `httponly`:默认是`False`。如果为`True`,那么在客户端不能通过`JavaScript`进行操作。
#### 删除cookie:
通过`delete_cookie`即可删除`cookie`。实际上删除`cookie`就是将指定的`cookie`的值设置为空的字符串,然后使用将他的过期时间设置为`0`,也就是浏览器关闭后就过期。
#### 获取cookie:
获取浏览器发送过来的`cookie`信息。可以通过`request.COOKIES`来或者。这个对象是一个字典类型。比如获取所有的`cookie`,那么示例代码如下:
```python
cookies = request.COOKIES
for cookie_key,cookie_value in cookies.items():
print(cookie_key,cookie_value)
```
------
### 操作session:
`django`中的`session`默认情况下是存储在服务器的数据库中的,在表中会根据`sessionid`来提取指定的`session`数据,然后再把这个`sessionid`放到`cookie`中发送给浏览器存储,浏览器下次在向服务器发送请求的时候会自动的把所有`cookie`信息都发送给服务器,服务器再从`cookie`中获取`sessionid`,然后再从数据库中获取`session`数据。但是我们在操作`session`的时候,这些细节压根就不用管。我们只需要通过`request.session`即可操作。示例代码如下:
```python
def index(request):
request.session.get('username')
return HttpResponse('index')
def session_view(request):
request.session['username'] = "kangbazi1806" #如果登录 验证通过 同时将信息存到session中 一份
request.session['userid'] = '1806'
# username = request.session.pop("username")
# userid = request.session.pop("userid")
request.session.clear()
request.session.flush()
username = request.session.get("username")
print(username)
# expire = datetime(year=2018,month=9,day=25,hour=20,minute=30,second=0)
# expires = make_aware(expire)
# request.session.set_expiry(expires)
return HttpResponse("session view")
```
`session`常用的方法如下:
1. `get`:用来从`session`中获取指定值。
2. `pop`:从`session`中删除一个值。
3. `keys`:从`session`中获取所有的键。
4. `items`:从`session`中获取所有的值。
5. `clear`:清除当前这个用户的`session`数据。
6. `flush`:删除`session`并且删除在浏览器中存储的`session_id`,一般在注销的时候用得比较多。
7. `set_expiry(value)`:设置过期时间。
- 整形:代表秒数,表示多少秒后过期。
- `0`:代表只要浏览器关闭,`session`就会过期。
- `None`:会使用全局的`session`配置。在`settings.py`中可以设置`SESSION_COOKIE_AGE`来配置全局的过期时间。默认是`1209600`秒,也就是2周的时间。
8. `clear_expired`:清除过期的`session`。`Django`并不会清除过期的`session`,需要定期手动的清理,或者是在终端,使用命令行`python manage.py clearsessions`来清除过期的`session`。
### 修改session的存储机制:下面这些 在 settings.py 中进行设置 session存储方式 很多 不止一种
> 最后一行 加入 SESSION_ENGINE = "django.contrib.sessions.backends.cache"
默认情况下,`session`数据是存储到数据库中的。当然也可以将`session`数据存储到其他地方。可以通过设置`SESSION_ENGINE`来更改`session`的存储位置,这个可以配置为以下几种方案:
1. `django.contrib.sessions.backends.db`:使用数据库。默认就是这种方案。django_session
2. `django.contrib.sessions.backends.file`:使用文件来存储session。
3. `django.contrib.sessions.backends.cache`:使用缓存来存储session。想要将数据存储到缓存中,前提是你必须要在`settings.py`中配置好`CACHES`,并且是需要使用`Memcached`,而不能使用纯内存作为缓存。
4. `django.contrib.sessions.backends.cached_db`:在存储数据的时候,会将数据先存到缓存中,再存到数据库中。这样就可以保证万一缓存系统出现问题,session数据也不会丢失。在获取数据的时候,会先从缓存中获取,如果缓存中没有,那么就会从数据库中获取。
5. `django.contrib.sessions.backends.signed_cookies`:将`session`信息加密后存储到浏览器的`cookie`中。这种方式要注意安全,建议设置`SESSION_COOKIE_HTTPONLY=True`,那么在浏览器中不能通过`js`来操作`session`数据,并且还需要对`settings.py`中的`SECRET_KEY`进行保密,因为一旦别人知道这个`SECRET_KEY`,那么就可以进行解密。另外还有就是在`cookie`中,存储的数据不能超过`4k`。
# 登录注册验证代码实例
~~~
app:front
models.py
from django.db import models
from django.core.validators import MinLengthValidator
class User(models.Model):
username = models.CharField(max_length=100,validators=[MinLengthValidator(6)])
password = models.CharField(max_length=16,validators=[MinLengthValidator(6)])
telephone = models.CharField(max_length=11)
forms.py
from django import forms
from .models import User
class RegisterForm(forms.ModelForm):
password_repeat = forms.CharField(max_length=16,min_length=6)
def clean(self):
# 以后遇到表单验证中 判断两个字段是否相等
# 等待验证成功以后再判断这个时候需要重写clean方法 继承于父clean方法
#super().clean()示例代码中的调用确保维护父类中的任何验证逻辑
clean_data = super().clean()
password = clean_data.get("password")
password_repeat = clean_data.get("password_repeat")
if password != password_repeat:
raise forms.ValidationError(message="两次密码不一致")
return clean_data
class Meta:
model = User
fields = "__all__"
class LoginForm(forms.ModelForm):
class Meta:
model = User
fields = ['username','password']
error_messages = {
"username":{
'min_length':'用户名不能少于6位'
},
"password":{
'min_length':"密码不能少于6位"
}
}
views.py
from django.shortcuts import render,redirect,reverse
from django.views.generic import View
from .models import User
from django.http import HttpResponse
from .forms import RegisterForm,LoginForm
def index(request):
userid = request.session.get('user_id')
user = User.objects.get(pk=userid)
context = {
"username":user
}
return render(request,'index.html',context=context)
class RegisterView(View):
def get(self,request):
return render(request,'register.html')
def post(self,request):
forms = RegisterForm(request.POST)
if forms.is_valid():
telephone = forms.cleaned_data.get('telephone')
user = User.objects.filter(telephone=telephone).first()
if not user:
forms.save()
else:
return HttpResponse("手机号已存在")
return redirect(reverse('index'))
else:
print(forms.errors.get_json_data())
return redirect(reverse('register'))
class LoginView(View):
def get(self,request):
return render(request,'login.html')
def post(self,request):
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = User.objects.first(username=username,password=password).first()
if user:
request.sessiom['user_id'] = user.id #登录成功以后 将这个用户的id存到session
return redirect(reverse('index'))
else:
print("用户名或密码错误")
return redirect(reverse('login'))
else:
errors = form.errors.get_json_data()
print(errors)
return redirect(reverse('login'))
def blog(request):
return render(request,'blog.html')
urls.py
from django.urls import path
from front import views
urlpatterns = [
path('', views.index,name='index'),
path('register/', views.RegisterView.as_view(),name="register"),
path('login/', views.LoginView.as_view(),name="login"),
path('blog/', views.LoginView.as_view(),name="blog"),
]
base.html
login.html
{% extends 'base.html' %}
{% block body %}
index.html
{% extends 'base.html' %}
{% block body %}
首页
{% endblock %}
blog.html
{% extends 'base.html' %}
{% block body %}
博客页面
{% endblock %}
~~~