django -- 文件上传&cookie/session&登录注册

# 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 %}
       


           
               
                   
                       
                       
                   
                   
                       
                       
                   
                   
                       
                   
               
           
用户名:
密码:

       

    {% endblock %}
    
register.html
    {% extends 'base.html' %}
    {% block body %}
       

           
               
                   
                       
                       
                   
                   
                       
                       
                   
                   
                       
                       
                   
                   
                       
                       
                   
                   
                       
                   
               
           
用户名:
Tel:
密码:
确认密码:

       

    {% endblock %}

index.html
    {% extends 'base.html' %}
    {% block body %}
        首页
    {% endblock %}
    
blog.html
    {% extends 'base.html' %}
    {% block body %}
        博客页面
    {% endblock %}
~~~

你可能感兴趣的:(Django,html)