django_django 项目 - CSDN
django 订阅
《被解救的姜戈》是由美国韦恩斯坦国际影业公司出品的西部动作片,由昆汀·塔伦蒂诺执导,杰米·福克斯、克里斯托弗·瓦尔兹、莱昂纳多·迪卡普里奥、塞缪尔·杰克逊、凯丽·华盛顿领衔主演。该片讲述了德国医生金·舒尔茨将黑奴姜戈从奴隶贩子手里解救,并且帮助姜戈救出他的妻子。该片于2012年12月25日在美国上映,2013年5月12日在中国上映 [1]  。 展开全文
《被解救的姜戈》是由美国韦恩斯坦国际影业公司出品的西部动作片,由昆汀·塔伦蒂诺执导,杰米·福克斯、克里斯托弗·瓦尔兹、莱昂纳多·迪卡普里奥、塞缪尔·杰克逊、凯丽·华盛顿领衔主演。该片讲述了德国医生金·舒尔茨将黑奴姜戈从奴隶贩子手里解救,并且帮助姜戈救出他的妻子。该片于2012年12月25日在美国上映,2013年5月12日在中国上映 [1]  。
信息
主    演
杰米·福克斯,莱昂纳多·迪卡普里奥,克里斯托弗·瓦尔兹,凯莉·华盛顿,塞缪尔·杰克逊
上映时间
2012年12月25日(美国)
色    彩
彩色
制片成本
1亿美元
导    演
昆汀·塔伦蒂诺
其它译名
被解放的迪亚戈、黑杀令(台湾)、决杀令(香港)
类    型
西部、剧情、冒险、动作
分    级
USA:R
出品公司
韦恩斯坦国际影业公司、哥伦比亚电影公司(美国)
拍摄日期
2012年1月
中文名
被解救的姜戈
片    长
165分钟
发行公司
韦恩斯坦国际影业公司(美国)
imdb编码
tt1853728
外文名
Django Unchained
编    剧
昆汀·塔伦蒂诺
对白语言
英语、德语、法语、意大利语
出品时间
2012年
制片人
雷吉纳·哈德林、皮拉尔·萨富隆
制片地区
美国
数据分析简介
收起全文
精华内容
参与话题
  • 本系列课程是从零基础开始并深入讲解Django,最终学会使用Django框架开发企业级的项目。课程知识点全网最详细,项目实战贴近企业需求。本系列课程除了非常详细的讲解Django框架本身的知识点以外,还讲解了web开发中...
  • Django实例 —— 搭建一个博客(超详细)

    万次阅读 多人点赞 2019-01-03 19:31:09
    # django-admin startproject 项目名称 django-admin startproject mysite  进入mysite目录,工程目录结构默认如图 与项目同名的目录,此处为mysite,包含项目的配置文件  manage.py : Django 项目里面的...

    创建mysite工程项目,创建项目的命令如下:

    # django-admin startproject 项目名称
    django-admin startproject mysite

     进入mysite目录,工程目录结构默认如图

    与项目同名的目录,此处为mysite,包含项目的配置文件 

    • manage.py : Django 项目里面的工具,是项目运行的入口,指定配置文件路径,通过它可以调用django shell和数据库等。
    • setting.py : 包含了项目的默认配置,包括数据库信息,调试标志以及其他一些工作的变量,是项目的整体配置文件。
    • urls.py : 负责把URL模式映射到应用程序,是项目的URL配置文件。
    • init.py是一个空文件,作用是这个目录test1可以被当作包使用。
    • wsgi.py是项目与WSGI兼容的Web服务器入口。

    运行服务

    • 运行服务器命令如下:
    # python manage.py runserver ip:端口
    python manage.py runserver
    
    • 可以不写ip
    • 默认端口为8000
    • 服务器成功启动后如下图

    浏览器访问 http://127.0.0.1:8000/,运行结果如图所示

     

    创建blog应用

    python manage.py startapp blog

    blog目录结构如图所示

     

    • init.py是一个空文件,表示当前目录blog可以当作一个python包使用
    • tests.py文件用于开发测试用例,在实际开发中会有专门的测试人员,这个事情不需要我们来做

    安装添加应用

    • 应用创建成功后,需要安装才可以使用
    • 在mysite/settings.py文件中INSTALLED_APPS下添加应用的名称就可以完成安装
    • 在元组列表末尾中添加一个新的项,当前示例为blog

     

     

    设计模型

    现在打开blog目录下的models.py文件,这是定义blog数据结构的地方。

    from django.db import models
    
    # Create your models here.
    class BlogPost(models.Model):
        # 博客标题
        title=models.CharField(max_length=150)
        # 博客正文
        body=models.TextField()
        # 博客创建时间
        timestamp=models.DateTimeField()

    创建BlogPost博客类,继承django.db.models.Model父类,定义3个变量:title(博客标题)、body(博客正文)、timestamp(博客创建时间)

    迁移

    • 默认采用sqlite3数据库
    • 迁移前目录结构如下图:

    • 迁移由两步完成
      • 1.生成迁移文件:根据模型类生成创建表的语句
      • 2.执行迁移:根据第一步生成的语句在数据库中创建表
    • 生成迁移文件命令如下:
    python manage.py makemigrations

     

    • 执行迁移命令如下:
    python manage.py migrate

    • 迁移后目录结构如下图:

    管理界面本地化

    • 本地化是将显示的语言、时间等使用本地的习惯,这里的本地化就是进行中国化,中国大陆地区使用简体中文,时区使用亚洲/上海时区,注意这里不使用北京时区表示
    • 打开mysite/settings.py文件,找到语言编码、时区的设置项,将内容改为如下
    LANGUAGE_CODE = 'zh-Hans'
    TIME_ZONE = 'Asia/Shanghai'

    创建管理员

    • 创建管理员的代码如下,按提示输入用户名、邮箱、密码
    python manage.py createsuperuser

    • 启动服务器
    python manage.py runserver
    • 打开浏览器,在地址栏中输入如下地址后回车
    http://127.0.0.1:8000/admin
    • 输入前面创建的用户名、密码完成登录

    • 登录成功后界面如下,但是并没有BlogPost的管理入口,接下来进行增加BlogPost的管理入口操作

     

    注册模型类

    • 登录后台管理后,默认没有我们创建的应用中定义的模型类,需要在自己应用中的admin.py文件中注册,才可以在后台管理中看到,并进行crud操作
    • 打开booktest/admin.py文件,编写如下代码
    from django.contrib import admin
    from blog.models import BlogPost
    
    admin.site.register(BlogPost)
    
    • 到浏览器中刷新页面,可以看到模型类BlogPost的管理了

     

    • 点击类名称“BookInfo”可以进入列表页,

    • 在列表页中点击“增加”可以进入增加页,Django会根据模型类的不同,生成不同的表单控件,按提示填写表单内容后点击"保存",完成数据创建,创建成功后返回列表页

     自定义管理页面

    • Django提供了自定义管理页面的功能,比如列表页要显示哪些值
    • 打开mysite/admin.py文件,自定义类,继承自admin.ModelAdmin类
    • 属性list_display表示要显示哪些属性

    自定义管理页面之前

    打开mysite/admin.py文件,自定义类,继承自admin.ModelAdmin类

    from django.contrib import admin
    from blog.models import BlogPost
    
    class BlogPostAdmin(admin.ModelAdmin):
        # pk:索引
        # 属性list_display表示要显示哪些属性
        list_display = ['pk','title','body','timestamp']
    
    admin.site.register(BlogPost,BlogPostAdmin)
    

     自定义管理界面之后

    创建blog的公共部分

     从django的角度来看,一个页面具有3个典型的组件

    • templates (模板) :模板负责把传递进来的信息显示出来
    • view (视图) :视图负责从数据库获取需要显示的数据
    • url (URL模式) :URL模式负责把收到的请求与视图函数匹配,有时也会向视图传递一些参数

    创建模板

    在blog项目中创建templates文件夹目录(mysite/blog/templates/)在目录中创建模板文件archive.html,代码如下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% for post in posts %}
        <h2>{{ post.title }}</h2>
        <p>{{ post.timestamp }}</p>
        <p>{{ post.body }}</p>
    {% endfor %}
    </body>
    </html>

    设置模板路径,打开mysite/settings.py文件,修改设置

    创建视图函数

    打开mysite/blog/views.py文件

    from django.shortcuts import render
    from blog.models import BlogPost
    
    def archive(request):
        posts=BlogPost.objects.all()
        return render(request, 'archive.html', {'posts': posts})

    posts=BlogPost.objects.all()  :获取数据库里面所有的BlogPost对象

    return render(request, 'archive.html', {'posts': posts})  :渲染模板,数据是一个字典型的对象(键值对)

    创建blog的URL模式

    在mysite/urls.py文件中添加blog的URL

    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import include,url
    urlpatterns = [
        url(r'^blog/',include('blog.urls')),
        path('admin/', admin.site.urls),
    ]
    

    在mysite/blog/目录下创建urls.py文件

    from django.conf.urls import url
    from . import views
    
    urlpatterns=[
        url(r'^archive$',views.archive),
    ]

    再次启动服务python manage.py runserver,访问http://127.0.0.1:8000/blog/archive

    页面如图所示

     这样一个简单的博客就搭建完成了

    展开全文
  • Python+Django框架Web应用开发

    千人学习 2019-10-31 11:29:41
    系统介绍使用Django框架开发Web应用程序,当然,依然水煮风格
  • 记录一次完整的基于django的实例

    万次阅读 2018-08-03 17:53:14
    win10 + pycharm + python3.5 + django1.10 项目配置流程: 安装pymysql→Settings配置→(databases,templates下的dirs,新建statixfiles_dirs)→migration生成数据表→编写views.py→配置urls.py...

    实例内容:
    一个简单的将前端页面数据提交到数据库,从数据库取出数据展示到前端页面。

    环境:
    win10 + pycharm + python3.5 + django1.10

    项目配置流程:
    安装pymysql→Settings配置→(databases,templates下的dirs,新建statixfiles_dirs)→migration生成数据表→编写views.py→配置urls.py→(1.HTNL个css文件分离,2.css文件分离与地址修改)

    第一步:创建项目djangostart

    Paste_Image.png

    第二步:创建app-> message

    首先按照图所示,调出运行窗口

    Paste_Image.png

    然后在窗口中输入:startapp message
    这时会生成对应的app,并将app添加到setting配置文件。

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    
        'message',
    ]
    

    第三步:创建static目录,用于存放静态文件(CSS,JS等文件)

    Paste_Image.png

    至此我们已经创建好项目的基本目录。

    第四步:将前端页面配置到模板,将CSS文件配置到static目录

    目录结构为

    前端页面代码逻辑为;

    <head>
        <meta charset="UTF-8">
        <title></title>
        <link rel="stylesheet" href="/static/css/style.css">
    </head>
    

    第五步:配置setting文件中的静态文件和模板文件路径

    当配置好第四步之后,我们如果运行程序不会加载出页面,因为我们需要配置文件路径。
    对于静态文件:

    STATIC_URL = '/static/'
    #仅有上面的设置,只说明了样式放在static文件,需要指定和项目根目录的关系
    
    #设置静态文件的目录,设置为列表形式
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR,'static')
    ]
    

    对于模板文件:

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            #根据根目录设置templates目录
            'DIRS': [os.path.join(BASE_DIR, 'templates')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    

    其中的BASE_DIR为当前目录的绝对路径

    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    #获取当前目录的绝对路径
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    

    配置完这两项,我们就可以加载出页面,否则加载不出。

    第六步:配置django连接mysql数据库

    因为我们的项目是需要连接数据库的,因此我们需要配置连接数据库。
    python3连接MySQL不再使用MySQLdb,现在大部分都是使用PyMySQL用于连接MySQL数据库。
    首先安装PyMySQL用于替代MySQLdb。
    然后在工程目录的init.py中填写下面两句话

    import pymysql
    pymysql.install_as_MySQLdb()
    

    接着配置setting文件,连接数据库

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql', #主要是这里,将默认的sqlite3改为mysql
            'NAME': "testdjango", #数据库的名字
            'USER': "root",#数据库用户名
            'PASSWORD': "root",#数据库密码
            'HOST': "127.0.0.1",数据库地址,默认本机
            'PORT': "",数据库端口,默认3306
        }
    }
    

    配置好数据库之后,可以根据django来生成默认的数据表。
    同样是在Tools -> Run manage.py Task 打开运行窗口
    我们运行makemigrations(检查要修改的数据库字段),migrate(生成数据表)
    当运行完上述两个命令之后,就会在数据库中创建一些默认的基本数据表。

    第七步:配置urls.py和views.py

    在views.py中配置函数

    def getform(request):
        return  render(request,'message.html')
    

    在urls.py中配置url映射

    from django.conf.urls import url
    from django.contrib import admin
    from message.views import getform
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^form/$',getform,name='go_form')
    ]
    

    配置好以上两个文件,我们就可以调试程序了,Run -> Debug
    然后在浏览器中输入:<a>http://127.0.0.1:8000/form/</a>

    Paste_Image.png

    第八步:django model层的配置

    我们使用django的ORM机制,使类和数据库里的表相对应。
    我们在models.py中进行以下配置:

    # Create your models here.
    class UserMessage(models.Model):
        object_id = models.CharField(primary_key=True,verbose_name=u"主键",max_length=20,default="")
        name = models.CharField(max_length=20,null=True,blank=True,default="",verbose_name=u"用户名")
        email = models.EmailField(verbose_name=u"邮箱")
        address = models.CharField(max_length=100,verbose_name=u"联系地址")
        message = models.CharField(max_length=500,verbose_name=u"留言信息")
    
        class Meta:
            verbose_name = u"用户留言信息"
            verbose_name_plural = verbose_name
            #指定数据库的表名
            #db_table = "user_message"
            #排序
            #ordering = "-object_id"
    

    根据前端页面表单的数据,我们创建了四个属性。
    里面的object_id 为主键。
    配置好之后,我们配置生成数据表,Tools -> Run manage.py Task 打开运行窗口:

    makemigrations  message
    之后运行
    migrate message
    

    我们可以通过navicat查看生成的数据表:

    Paste_Image.png

    第九步:通过ORM对数据库进行增删改查

    首先我们在views.py中引用我们刚才创建的类

    from .models import UserMessage
    

    如何获取数据库中的数据:
    我们通过类UserMessage的数据表管理器objects获取数据。

    获取数据库的所有数据,返回的是可以进行循环的QuerySet类型    数据表管理器objects
    all_message = UserMessage.objects.all()
    for message in all_message:
        print(message.name)
    这里的message其实就是UserMessage的实例
    我们还可以根据条件取出数据
    all_message = UserMessage.objects.filter(name=u"王二小",address=u"杭州")
    

    如何删除数据:
    我们可以使用delete()函数删除所取的值

    all_message = UserMessage.objects.all()
    #删除所有
    #all_message.delete()
    for message in all_message:
        #删除单一值
        #message.delete()
    

    如何往数据库里面添加数据:
    我们通过实例化UserMessage对象,通过属性赋值,通过save()方法往数据库里赋值。

    user_message = UserMessage()
    user_message.name = u"王小二"
    user_message.message = u"放羊娃"
    user_message.address = u"杭州"
    user_message.object_id = "2"
    user_message.email = "2@2.com"
    user_message.save()
    

    完成上面的代码之后,我们运行项目,然后刷新页面,这时数据将会保存进数据库。

    如何提取前端页面表单中的数据保存到数据库中:
    我们使用POST将表单数据提交。

    #首先判断request的方式
    if request.method == "POST":
        #通过request的get()函数,获得提交的值
        name = request.POST.get('name','')#当属性值不存在,则赋空值
        message = request.POST.get('message','')
        email  = request.POST.get('email','')
        address = request.POST.get('address','')
    
        user_message = UserMessage()
        user_message.name = name
        user_message.message = message
        user_message.address = address
        user_message.object_id = "3" #这里的主键我们随便设置
        user_message.email = email
    
        #将值保存到数据库
        user_message.save()
    

    我们提交的数据都保存在POST中,通过get方法获得。
    代码中get('name','')这里的name是前端页面表单里面name="name"的name。

    想要完成数据的提交在前端页面要有下面两项配置:

    <form action="/form/}" method="post" class="smart-green">
    

    以及表单的提交允许

        #加了csrf_token之后才能够往后台提交数据
        {% csrf_token %}
    

    完成上面的代码配置之后,运行调试,然后在表单中输入数据然后提交,数据被保存到数据库中。

    如何将数据库中的数据显式到前端页面中:
    通过render()方法,添加字典形式的参数。

        #将后台数据库里的数据提取到前端页面
        message = None
        all_message = UserMessage.objects.filter(name=u"王二小")
        返回的Queryset可以做切片操作
        if all_message:
            message = all_message[0]
    
        return  render(request,'message.html',{"my_message":message})
    

    在前端页面中,我们使用的是字典的键。
    在前端页面的调用:

    value="{{ my_message.name }}"
    

    好了,至此为止,我们基本完成了一个简单的表单提交(里面有很多坑)。还有url的命名等其它知识



    作者:海贼之路飞
    链接:https://www.jianshu.com/p/335121af76d3
    來源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    展开全文
  • Django用户登录与注册系统

    万次阅读 多人点赞 2018-06-04 10:00:05
    1.1.创建项目和appdjango-admin startproject mysite_...设置时区和语言Django默认使用美国时间和英语,在项目的settings文件中,如下所示:LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L1...

    1.1.创建项目和app

    django-admin startproject mysite_login
    
    python manage.py startapp login

    1.2.设置时区和语言

    Django默认使用美国时间和英语,在项目的settings文件中,如下所示:

    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True

    我们把它改为亚洲/上海时间和中文

    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = False

    1.3.启动

    运行测试一下工程,在本机的浏览器中访问http://127.0.0.1:8000/

     二、设计数据模型

     2.1.数据库模型设计

     作为一个用户登录和注册项目,需要保存的都是各种用户的相关信息。很显然,我们至少需要一张用户表User,在用户表里需要保存下面的信息:

    • 用户名
    • 密码
    • 邮箱地址
    • 性别
    • 创建时间

     进入login/models.py,代码如下

    # login/models.py
    
    from django.db import models
    
    
    class User(models.Model):
        '''用户表'''
    
        gender = (
            ('male','男'),
            ('female','女'),
        )
    
        name = models.CharField(max_length=128,unique=True)
        password = models.CharField(max_length=256)
        email = models.EmailField(unique=True)
        sex = models.CharField(max_length=32,choices=gender,default='男')
        c_time = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return self.name
    
        class Meta:
            ordering = ['c_time']
            verbose_name = '用户'
            verbose_name_plural = '用户'
        

    各字段含义:

    • name必填,最长不超过128个字符,并且唯一,也就是不能有相同姓名;
    • password必填,最长不超过256个字符(实际可能不需要这么长);
    • email使用Django内置的邮箱类型,并且唯一;
    • 性别使用了一个choice,只能选择男或者女,默认为男;
    • 使用__str__帮助人性化显示对象信息;
    • 元数据里定义用户按创建时间的反序排列,也就是最近的最先显示;

    注意:这里的用户名指的是网络上注册的用户名,不要等同于现实中的真实姓名,所以采用了唯一机制。如果是现实中可以重复的人名,那肯定是不能设置unique的。

     2.2.设置数据库为Mysql

    在settings.py修改

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'django',        #数据库名字
            'USER': 'root',          #账号
            'PASSWORD': '123456',      #密码
            'HOST': '127.0.0.1',    #IP
            'PORT': '3306',                   #端口
        }
    }

    init.py里面导入pymysql模块

    # login/init.py
    
    import pymysql
    pymysql.install_as_MySQLdb()

      2.3.数据库迁移

    注册app

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'login',
    ]

    迁移到数据库

    python manage.py makemigrations
    
    python manage.py migrate

    三、admin后台

    3.1.在admin中注册模型

    # login/admin.py
    
    from django.contrib import admin
    from . import models
    
    admin.site.register(models.User)

     3.2.创建超级管理员

    python manage.py createsuperuser

    然后再增加几个测试用户

     四、url路由和视图

     前面我们已经创建好数据模型了,并且在admin后台中添加了一些测试用户。下面我们就要设计好站点的url路由、对应的处理视图函数以及使用的前端模板了。

     4.1.路由设计

    初步设想需要下面的四个URL:

    考虑到登录系统属于站点的一级功能,为了直观和更易于接受,这里没有采用二级路由的方式,而是在根路由下直接编写路由条目,同样也没有使用反向解析名(name参数)。

    # mysite_login/urls.py
    
    from django.conf.urls import url
    from django.contrib import admin
    from login import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.index),
        url(r'^login/', views.login),
        url(r'^register/', views.register),
        url(r'^logout/', views.logout),
    ]

    4.2.架构初步视图

    路由写好了,就进入login/views.py文件编写视图的框架,代码如下:

    # login/views.py
    
    from django.shortcuts import render,redirect
    
    def index(request):
        pass
        return render(request,'login/index.html')
    
    def login(request):
        pass
        return render(request,'login/login.html')
    
    def register(request):
        pass
        return render(request,'login/register.html')
    
    def logout(request):
        pass
        return redirect('/index/')

    我们先不着急完成视图内部的具体细节,而是把框架先搭建起来。

    4.3.创建HTML页面文件

    在项目根路径的login目录中创建一个templates目录,再在templates目录里创建一个login目录

    login/templates/login目录中创建三个文件index.htmllogin.html以及register.html ,并写入如下的代码:

    index.html

    {#login/templates/login/index.html#}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
    <h1>首页</h1>
    </body>
    </html>

    login.html

    {#login/templates/login/login.html#}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
    <h1>登录页面</h1>
    </body>
    </html>

    register.html

    {#login/templates/login/register.html#}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册</title>
    </head>
    <body>
    <h1>注册页面</h1>
    </body>
    </html>

     五、前端页面设计

     5.1.原生HTML页面

    login.html文件中的内容,写入下面的代码:

    {#login/templates/login/login.html#}
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
         <div style="margin: 15% 40%;">
            <h1>欢迎登录!</h1>
           <form action="/login/" method="post">
                <p>
                    <label for="id_username">用户名:</label>
                    <input type="text" id="id_username" name="username" placeholder="用户名" autofocus required />
                </p>
              
                <p>
                    <label for="id_password">密码:</label>
                    <input type="password" id="id_password" placeholder="密码" name="password" required >
                </p>
                <input type="submit" value="确定">
            </form>
        </div>
    
    
    
    </body>
    </html>

    可以看到如下图的页面:

    5.2.引入Bootstrap

    Bootstrap3.3.7下载地址

    根目录下新建一个static目录,并将解压后的bootstrap-3.3.7-dist目录,整体拷贝到static目录中,如下图所示:

    由于Bootstrap依赖JQuery,所以我们需要提前下载并引入JQuery下载地址

    在static目录下,新建一个css和js目录,作为以后的样式文件和js文件的存放地,将我们的jquery文件拷贝到static/js目录下。

    然后打开项目的settings文件,在最下面添加配置,用于指定静态文件的搜索目录:

    STATIC_URL = '/static/'
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, "static"),
    ]

    5.3.创建base.html模板

    既然要将前端页面做得像个样子,那么就不能和前面一样,每个页面都各写各的,单打独斗。一个网站有自己的统一风格和公用部分,可以把这部分内容集中到一个基础模板base.html中。现在,在根目录下的templates中新建一个base.html文件用作站点的基础模板。

    在Bootstrap文档中,为我们提供了一个非常简单而又实用的基本模板,代码如下:

    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
        <title>Bootstrap 101 Template</title>
    
        <!-- Bootstrap -->
        <link href="css/bootstrap.min.css" rel="stylesheet">
    
        <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
        <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
        <!--[if lt IE 9]>
          <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
          <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
        <![endif]-->
      </head>
      <body>
        <h1>你好,世界!</h1>
    
        <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
        <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
        <!-- Include all compiled plugins (below), or include individual files as needed -->
        <script src="js/bootstrap.min.js"></script>
      </body>
    </html>

    将它整体拷贝到base.html文件中。

     5.4.创建页面导航条

    Bootstrap提供了现成的导航条组件

    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Brand</a>
        </div>
    
        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
            <li><a href="#">Link</a></li>
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
              <ul class="dropdown-menu">
                <li><a href="#">Action</a></li>
                <li><a href="#">Another action</a></li>
                <li><a href="#">Something else here</a></li>
                <li role="separator" class="divider"></li>
                <li><a href="#">Separated link</a></li>
                <li role="separator" class="divider"></li>
                <li><a href="#">One more separated link</a></li>
              </ul>
            </li>
          </ul>
          <form class="navbar-form navbar-left">
            <div class="form-group">
              <input type="text" class="form-control" placeholder="Search">
            </div>
            <button type="submit" class="btn btn-default">Submit</button>
          </form>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="#">Link</a></li>
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
              <ul class="dropdown-menu">
                <li><a href="#">Action</a></li>
                <li><a href="#">Another action</a></li>
                <li><a href="#">Something else here</a></li>
                <li role="separator" class="divider"></li>
                <li><a href="#">Separated link</a></li>
              </ul>
            </li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->
    </nav>

    其中有一些部分,比如搜索框是我们目前还不需要的,需要将多余的内容裁剪掉。同时,有一些名称和url地址等需要按我们的实际内容修改。最终导航条的代码如下:

    <nav class="navbar navbar-default">
          <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav" aria-expanded="false">
                <span class="sr-only">切换导航条</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="#">Mysite</a>
            </div>
    
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="my-nav">
              <ul class="nav navbar-nav">
                <li class="active"><a href="/index/">主页</a></li>
              </ul>
              <ul class="nav navbar-nav navbar-right">
                <li><a href="/login/">登录</a></li>
                <li><a href="/register/">注册</a></li>
              </ul>
            </div><!-- /.navbar-collapse -->
          </div><!-- /.container-fluid -->
        </nav>

    5.5.使用Bootstrap静态文件

    {% static '相对路径' %}这个Django为我们提供的静态文件加载方法,可以将页面与静态文件链接起来

     最后,base.html内容如下:

    {% load staticfiles %}
    
    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
        <title>{% block title %}base{% endblock %}</title>
    
        <!-- Bootstrap -->
        <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet">
    
        <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
        <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
        <!--[if lt IE 9]>
          <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
          <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
        <![endif]-->
        {% block css %}{% endblock %}
      </head>
      <body>
        <nav class="navbar navbar-default">
          <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav" aria-expanded="false">
                <span class="sr-only">切换导航条</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="#">Mysite</a>
            </div>
    
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="my-nav">
              <ul class="nav navbar-nav">
                <li class="active"><a href="/index/">主页</a></li>
              </ul>
              <ul class="nav navbar-nav navbar-right">
                <li><a href="/login/">登录</a></li>
                <li><a href="/register/">注册</a></li>
              </ul>
            </div><!-- /.navbar-collapse -->
          </div><!-- /.container-fluid -->
        </nav>
    
        {% block content %}{% endblock %}
    
    
        <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
        <script src="{% static 'js/jquery-3.2.1.js' %}"></script>
        <!-- Include all compiled plugins (below), or include individual files as needed -->
        <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
      </body>
    </html>

    简要说明:

    • 通过页面顶端的{% load staticfiles %}加载后,才可以使用static方法;
    • 通过{% block title %}base{% endblock %},设置了一个动态的页面title块;
    • 通过{% block css %}{% endblock %},设置了一个动态的css加载块;
    • 通过{% block content %}{% endblock %},为具体页面的主体内容留下接口;
    • 通过{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}将样式文件指向了我们的实际静态文件,下面的js脚本也是同样的道理。

     看下效果

    5.6.设计登录页面

    Bootstarp提供了一个基本的表单样式,代码如下:

    <form>
      <div class="form-group">
        <label for="exampleInputEmail1">Email address</label>
        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">
      </div>
      <div class="form-group">
        <label for="exampleInputPassword1">Password</label>
        <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
      </div>
      <div class="form-group">
        <label for="exampleInputFile">File input</label>
        <input type="file" id="exampleInputFile">
        <p class="help-block">Example block-level help text here.</p>
      </div>
      <div class="checkbox">
        <label>
          <input type="checkbox"> Check me out
        </label>
      </div>
      <button type="submit" class="btn btn-default">Submit</button>
    </form>

    如下:

    我们结合Bootstrap和前面自己写的form表单,修改login/templates/login/login.html成符合项目要求的样子:

    {% extends 'login/base.html' %}
    {% load staticfiles %}
    {% block title %}登录{% endblock %}
    {% block css %}
        <link rel="stylesheet" href="{% static 'css/login.css' %}">
    {% endblock %}
    
    
    {% block content %}
        <div class="container">
            <div class="col-md-4 col-md-offset-4">
              <form class='form-login' action="/login/" method="post">
                  <h2 class="text-center">欢迎登录</h2>
                  <div class="form-group">
                    <label for="id_username">用户名:</label>
                    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
                  </div>
                  <div class="form-group">
                    <label for="id_password">密码:</label>
                    <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
                  </div>
                  <button type="reset" class="btn btn-default pull-left">重置</button>
                  <button type="submit" class="btn btn-primary pull-right">提交</button>
              </form>
            </div>
        </div> <!-- /container -->
    {% endblock %}

    说明:

    • 通过{% extends 'base.html' %}继承了‘base.html’模板的内容;
    • 通过{% block title %}登录{% endblock %}设置了专门的title;
    • 通过block css引入了针对性的login.css样式文件;
    • 主体内容定义在block content内部
    • 添加了一个重置按钮。

    static/css目录中新建一个login.css样式文件,这里简单地写了点样式,

    body {
      background-color: #eee;
    }
    .form-login {
      max-width: 330px;
      padding: 15px;
      margin: 0 auto;
    }
    .form-login .form-control {
      position: relative;
      height: auto;
      -webkit-box-sizing: border-box;
         -moz-box-sizing: border-box;
              box-sizing: border-box;
      padding: 10px;
      font-size: 16px;
    }
    .form-login .form-control:focus {
      z-index: 2;
    }
    .form-login input[type="text"] {
      margin-bottom: -1px;
      border-bottom-right-radius: 0;
      border-bottom-left-radius: 0;
    }
    .form-login input[type="password"] {
      margin-bottom: 10px;
      border-top-left-radius: 0;
      border-top-right-radius: 0;
    }

    最后效果

     六、登录视图

    6.1.登录视图

    根据我们在路由中的设计,用户通过login.html中的表单填写用户名和密码,并以POST的方式发送到服务器的/login/地址。服务器通过login/views.py中的login()视图函数,接收并处理这一请求。

    我们可以通过下面的方法接收和处理请求:

    def login(request):
        if request.method == "POST":
            username = request.POST.get('username')
            password = request.POST.get('password')        return redirect('/index/')
        return render(request, 'login/login.html')

    还需要在前端页面的form表单内添加一个{% csrf_token %}标签:

    <form class='form-login' action="/login/" method="post">
      {% csrf_token %}
      <h2 class="text-center">欢迎登录</h2>
      <div class="form-group">
      ......
    </form>

    进入登录页面,输入用户名,密码然后跳转到index页面。

    6.2.数据验证

    通过唯一的用户名,使用Django的ORM去数据库中查询用户数据,如果有匹配项,则进行密码对比,如果没有匹配项,说明用户名不存在。如果密码对比错误,说明密码不正确。

    def login(request):
        if request.method == "POST":
            username = request.POST.get('username', None)
            password = request.POST.get('password', None)
            if username and password:  # 确保用户名和密码都不为空
                username = username.strip()
                # 用户名字符合法性验证
                # 密码长度验证
                # 更多的其它验证.....
                try:
                    user = models.User.objects.get(name=username)
                except:
                    return render(request, 'login/login.html')
                if user.password == password:
                    return redirect('/index/')
        return render(request, 'login/login.html')

    6.3.添加提示信息

    上面的代码还缺少很重要的一部分内容,提示信息!无论是登录成功还是失败,用户都没有得到任何提示信息,这显然是不行的。

    修改一下login视图:

    def login(request):
        if request.method == "POST":
            username = request.POST.get('username', None)
            password = request.POST.get('password', None)
            message = "所有字段都必须填写!"
            if username and password:  # 确保用户名和密码都不为空
                username = username.strip()
                # 用户名字符合法性验证
                # 密码长度验证
                # 更多的其它验证.....
                try:
                    user = models.User.objects.get(name=username)
                    if user.password == password:
                        return redirect('/index/')
                    else:
                        message = "密码不正确!"
                except:
                    message = "用户名不存在!"
            return render(request, 'login/login.html', {"message": message})
        return render(request, 'login/login.html')

    增加了message变量,用于保存提示信息。当有错误信息的时候,将错误信息打包成一个字典,然后作为第三个参数提供给render()方法。这个数据字典在渲染模板的时候会传递到模板里供你调用。

    为了在前端页面显示信息,还需要对login.html进行修改:

    {% extends 'login/base.html' %}
    {% load staticfiles %}
    {% block title %}登录{% endblock %}
    {% block css %}
        <link rel="stylesheet" href="{% static 'css/login.css' %}">
    {% endblock %}
    
    
    {% block content %}
        <div class="container">
            <div class="col-md-4 col-md-offset-4">
                <form class='form-login' action="/login/" method="post">
    
                    {% if message %}
                        <div class="alert alert-warning">{{ message }}</div>
                    {% endif %}
    
                    {% csrf_token %}
                    <h2 class="text-center">欢迎登录</h2>
                    <div class="form-group">
                        <label for="id_username">用户名:</label>
                        <input type="text" name='username' class="form-control" id="id_username" placeholder="Username"
                               autofocus required>
                    </div>
                    <div class="form-group">
                        <label for="id_password">密码:</label>
                        <input type="password" name='password' class="form-control" id="id_password" placeholder="Password"
                               required>
                    </div>
                    <button type="reset" class="btn btn-default pull-left">重置</button>
                    <button type="submit" class="btn btn-primary pull-right">提交</button>
                </form>
            </div>
        </div> <!-- /container -->
    {% endblock %}

    index.html主页模板也修改一下,删除原有内容,添加下面的代码:

    {#login/templates/login/index.html#}
    
    {% extends 'login/base.html' %}
    {% block title %}主页{% endblock %}
    {% block content %}
        <h1>欢迎回来!</h1>
    {% endblock %}

     七、Django表单

    Django的表单给我们提供了下面三个主要功能:

    • 准备和重构数据用于页面渲染;
    • 为数据创建HTML表单元素;
    • 接收和处理用户从表单发送过来的数据

     7.1.创建表单模型

    from django import forms
    
    
    class UserForm(forms.Form):
        username = forms.CharField(label="用户名", max_length=128)
        password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput)

    说明:

    • 要先导入forms模块
    • 所有的表单类都要继承forms.Form类
    • 每个表单字段都有自己的字段类型比如CharField,它们分别对应一种HTML语言中<form>内的一个input元素。这一点和Django模型系统的设计非常相似。
    • label参数用于设置<label>标签
    • max_length限制字段输入的最大长度。它同时起到两个作用,一是在浏览器页面限制用户输入不可超过字符数,二是在后端服务器验证用户输入的长度也不可超过。
    • widget=forms.PasswordInput用于指定该字段在form表单里表现为<input type='password' />,也就是密码输入框。

     7.2.修改视图

    使用了Django的表单后,就要在视图中进行相应的修改:

    # login/views.py
    
    from django.shortcuts import render,redirect
    from . import models
    from .forms import UserForm
    
    def index(request):
        pass
        return render(request,'login/index.html')
    
    def login(request):
        if request.method == "POST":
            login_form = UserForm(request.POST)
            message = "请检查填写的内容!"
            if login_form.is_valid():
                username = login_form.cleaned_data['username']
                password = login_form.cleaned_data['password']
                try:
                    user = models.User.objects.get(name=username)
                    if user.password == password:
                        return redirect('/index/')
                    else:
                        message = "密码不正确!"
                except:
                    message = "用户不存在!"
            return render(request, 'login/login.html', locals())
    
        login_form = UserForm()
        return render(request, 'login/login.html', locals())

    说明:

    • 对于非POST方法发送数据时,比如GET方法请求页面,返回空的表单,让用户可以填入数据;
    • 对于POST方法,接收表单数据,并验证;
    • 使用表单类自带的is_valid()方法一步完成数据验证工作;
    • 验证成功后可以从表单对象的cleaned_data数据字典中获取表单的具体值;
    • 如果验证不通过,则返回一个包含先前数据的表单给前端页面,方便用户修改。也就是说,它会帮你保留先前填写的数据内容,而不是返回一个空表!

    另外,这里使用了一个小技巧,Python内置了一个locals()函数,它返回当前所有的本地变量字典,我们可以偷懒的将这作为render函数的数据字典参数值,就不用费劲去构造一个形如{'message':message, 'login_form':login_form}的字典了。这样做的好处当然是大大方便了我们,但是同时也可能往模板传入了一些多余的变量数据,造成数据冗余降低效率。

    7.3.修改login界面

    Django的表单很重要的一个功能就是自动生成HTML的form表单内容。现在,我们需要修改一下原来的login.html文件:

    {% extends 'base.html' %}
    {% load staticfiles %}
    {% block title %}登录{% endblock %}
    {% block css %}<link href="{% static 'css/login.css' %}" rel="stylesheet"/>{% endblock %}
    
    
    {% block content %}
        <div class="container">
            <div class="col-md-4 col-md-offset-4">
              <form class='form-login' action="/login/" method="post">
    
                  {% if message %}
                      <div class="alert alert-warning">{{ message }}</div>
                  {% endif %}
                  {% csrf_token %}
                  <h2 class="text-center">欢迎登录</h2>
    
                  {{ login_form }}
    
                  <button type="reset" class="btn btn-default pull-left">重置</button>
                  <button type="submit" class="btn btn-primary pull-right">提交</button>
    
              </form>
            </div>
        </div> <!-- /container -->
    {% endblock %}

    浏览器生成的HTML源码

    重新启动服务器,刷新页面,如下图所示:

    7.4.手动渲染表单

    直接{{ login_form }}虽然好,啥都不用操心,但是界面真的很丑,往往并不是你想要的,如果你要使用CSS和JS,比如你要引入Bootstarps框架,这些都需要对表单内的input元素进行额外控制,那怎么办呢?手动渲染字段就可以了。

    可以通过{{ login_form.name_of_field }}获取每一个字段,然后分别渲染,如下例所示:

    <div class="form-group">
      {{ login_form.username.label_tag }}
      {{ login_form.username}}
    </div>
    <div class="form-group">
      {{ login_form.password.label_tag }}
      {{ login_form.password }}
    </div>

    然后,在form类里添加attr属性即可,如下所示修改login/forms.py

    from django import forms
    
    class UserForm(forms.Form):
        username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
        password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))

    再次刷新页面,就显示正常了!

     八、图片验证码

    为了防止机器人频繁登录网站或者破坏分子恶意登录,很多用户登录和注册系统都提供了图形验证码功能。

    验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试。

    图形验证码的历史比较悠久,到现在已经有点英雄末路的味道了。因为机器学习、图像识别的存在,机器人已经可以比较正确的识别图像内的字符了。但不管怎么说,作为一种防御手段,至少还是可以抵挡一些低级入门的攻击手段,抬高了攻击者的门槛。

    在Django中实现图片验证码功能非常简单,有现成的第三方库可以使用,我们不必自己开发(也要能开发得出来,囧)。这个库叫做django-simple-captcha

    8.1.安装captcha

    直接安装:pip install django-simple-captcha

    Django自动帮我们安装了相关的依赖库sixolefilePillow,其中的Pillow是大名鼎鼎的绘图模块。

    注册captcha

    在settings中,将‘captcha’注册到app列表里:

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'login',
        'captcha',
    ]

    captcha需要在数据库中建立自己的数据表,所以需要执行migrate命令生成数据表:

    python manage.py migrate

    8.2.添加url路由

    根目录下的urls.py文件中增加captcha对应的网址:

    from django.conf.urls import url
    from django.conf.urls import include
    from django.contrib import admin
    from login import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.index),
        url(r'^login/', views.login),
        url(r'^register/', views.register),
        url(r'^logout/', views.logout),
        url(r'^captcha', include('captcha.urls'))  # 增加这一行
    ]

    8.3.修改forms.py

    如果上面都OK了,就可以直接在我们的forms.py文件中添加CaptchaField了。

    from django import forms
    from captcha.fields import CaptchaField
    
    class UserForm(forms.Form):
        username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
        password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
        captcha = CaptchaField(label='验证码')

    需要提前导入from captcha.fields import CaptchaField,然后就像写普通的form字段一样添加一个captcha字段就可以了!

     8.4.修改login.html

     由于我们前面是手动生成的form表单,所以还要修改一下,添加captcha的相关内容,如下所示:

    {% extends 'login/base.html' %}
    {% load staticfiles %}
    {% block title %}登录{% endblock %}
    {% block css %}
        <link rel="stylesheet" href="{% static 'css/login.css' %}">
    {% endblock %}
    
    
    {% block content %}
        <div class="container">
            <div class="col-md-4 col-md-offset-4">
              <form class='form-login' action="/login/" method="post">
    
                  {% if message %}
                      <div class="alert alert-warning">{{ message }}</div>
                  {% endif %}
                  {% csrf_token %}
                  <h2 class="text-center">欢迎登录</h2>
                  <div class="form-group">
                      {{ login_form.username.label_tag }}
                      {{ login_form.username}}
                  </div>
                  <div class="form-group">
                      {{ login_form.password.label_tag }}
                      {{ login_form.password }}
                  </div>
    
                  <div class="form-group">
                      {{ login_form.captcha.errors }}
                      {{ login_form.captcha.label_tag }}
                      {{ login_form.captcha }}
                  </div>
    
                  <button type="reset" class="btn btn-default pull-left">重置</button>
                  <button type="submit" class="btn btn-primary pull-right">提交</button>
    
              </form>
            </div>
        </div> <!-- /container -->
    {% endblock %}

    这里额外增加了一条{{ login_form.captcha.errors }}用于明确指示用户,你的验证码不正确

    查看效果:

    其中验证图形码是否正确的工作都是在后台自动完成的,只需要使用is_valid()这个forms内置的验证方法就一起进行了,完全不需要在视图函数中添加任何的验证代码,非常方便快捷!

     九、session会话

            因为因特网HTTP协议的特性,每一次来自于用户浏览器的请求(request)都是无状态的、独立的。通俗地说,就是无法保存用户状态,后台服务器根本就不知道当前请求和以前及以后请求是否来自同一用户。对于静态网站,这可能不是个问题,而对于动态网站,尤其是京东、天猫、银行等购物或金融网站,无法识别用户并保持用户状态是致命的,根本就无法提供服务。你可以尝试将浏览器的cookie功能关闭,你会发现将无法在京东登录和购物。

    为了实现连接状态的保持功能,网站会通过用户的浏览器在用户机器内被限定的硬盘位置中写入一些数据,也就是所谓的Cookie。通过Cookie可以保存一些诸如用户名、浏览记录、表单记录、登录和注销等各种数据。但是这种方式非常不安全,因为Cookie保存在用户的机器上,如果Cookie被伪造、篡改或删除,就会造成极大的安全威胁,因此,现代网站设计通常将Cookie用来保存一些不重要的内容,实际的用户数据和状态还是以Session会话的方式保存在服务器端。

    Session依赖Cookie!但与Cookie不同的地方在于Session将所有的数据都放在服务器端,用户浏览器的Cookie中只会保存一个非明文的识别信息,比如哈希值。

    Django提供了一个通用的Session框架,并且可以使用多种session数据的保存方式:

    • 保存在数据库内
    • 保存到缓存
    • 保存到文件内
    • 保存到cookie内

    通常情况,没有特别需求的话,请使用保存在数据库内的方式,尽量不要保存到Cookie内。

    Django的session框架默认启用,并已经注册在app设置内,如果真的没有启用,那么参考下面的内容添加有说明的那两行,再执行migrate命令创建数据表,就可以使用session了。

    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',    # 这一行
        'django.contrib.messages',
        'django.contrib.staticfiles',
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',  # 这一行
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]

    当session启用后,传递给视图request参数的HttpRequest对象将包含一个session属性,就像一个字典对象一样。你可以在Django的任何地方读写request.session属性,或者多次编辑使用它。

    下面是session使用参考:

    class backends.base.SessionBase
            # 这是所有会话对象的基类,包含标准的字典方法:
            __getitem__(key)
                Example: fav_color = request.session['fav_color']
            __setitem__(key, value)
                Example: request.session['fav_color'] = 'blue'
            __delitem__(key)
                Example: del request.session['fav_color']  # 如果不存在会抛出异常
            __contains__(key)
                Example: 'fav_color' in request.session
            get(key, default=None)
                Example: fav_color = request.session.get('fav_color', 'red')
            pop(key, default=__not_given)
                Example: fav_color = request.session.pop('fav_color', 'blue')
     # 类似字典数据类型的内置方法
            keys()
            items()
            setdefault()
            clear()
    
    
            # 它还有下面的方法:
            flush()
                # 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。
    
            set_test_cookie()
                # 设置一个测试cookie,用于探测用户浏览器是否支持cookies。由于cookie的工作机制,你只有在下次用户请求的时候才可以测试。
            test_cookie_worked()
                # 返回True或者False,取决于用户的浏览器是否接受测试cookie。你必须在之前先调用set_test_cookie()方法。
            delete_test_cookie()
                # 删除测试cookie。
            set_expiry(value)
                # 设置cookie的有效期。可以传递不同类型的参数值:
            • 如果值是一个整数,session将在对应的秒数后失效。例如request.session.set_expiry(300) 将在300秒后失效.
            • 如果值是一个datetime或者timedelta对象, 会话将在指定的日期失效
            • 如果为0,在用户关闭浏览器后失效
            • 如果为None,则将使用全局会话失效策略
            失效时间从上一次会话被修改的时刻开始计时。
    
            get_expiry_age()
                # 返回多少秒后失效的秒数。对于没有自定义失效时间的会话,这等同于SESSION_COOKIE_AGE.
                # 这个方法接受2个可选的关键字参数
            • modification:会话的最后修改时间(datetime对象)。默认是当前时间。
            •expiry: 会话失效信息,可以是datetime对象,也可以是int或None
    
            get_expiry_date()
                # 和上面的方法类似,只是返回的是日期
    
            get_expire_at_browser_close()
                # 返回True或False,根据用户会话是否是浏览器关闭后就结束。
    
            clear_expired()
                # 删除已经失效的会话数据。
            cycle_key()
                # 创建一个新的会话秘钥用于保持当前的会话数据。django.contrib.auth.login() 会调用这个方法。

    9.1.使用session

    首先,修改login/views.py中的login()视图函数:

    def login(request):
        if request.session.get('is_login',None):
            return redirect('/index')
    
        if request.method == "POST":
            login_form = UserForm(request.POST)
            message = "请检查填写的内容!"
            if login_form.is_valid():
                username = login_form.cleaned_data['username']
                password = login_form.cleaned_data['password']
                try:
                    user = models.User.objects.get(name=username)
                    if user.password == password:
                        request.session['is_login'] = True
                        request.session['user_id'] = user.id
                        request.session['user_name'] = user.name
                        return redirect('/index/')
                    else:
                        message = "密码不正确!"
                except:
                    message = "用户不存在!"
            return render(request, 'login/login.html', locals())
    
        login_form = UserForm()
        return render(request, 'login/login.html', locals())

    通过下面的if语句,我们不允许重复登录:

    if request.session.get('is_login',None):
        return redirect("/index/")

    通过下面的语句,我们往session字典内写入用户状态和数据:

    request.session['is_login'] = True
    request.session['user_id'] = user.id
    request.session['user_name'] = user.name

    你完全可以往里面写任何数据,不仅仅限于用户相关!

    既然有了session记录用户登录状态,那么就可以完善我们的登出视图函数了:

    def logout(request):
        if not request.session.get('is_login', None):
            # 如果本来就未登录,也就没有登出一说
            return redirect("/index/")
        request.session.flush()
        # 或者使用下面的方法
        # del request.session['is_login']
        # del request.session['user_id']
        # del request.session['user_name']
        return redirect("/index/")

    flush()方法是比较安全的一种做法,而且一次性将session中的所有内容全部清空,确保不留后患。但也有不好的地方,那就是如果你在session中夹带了一点‘私货’,会被一并删除,这一点一定要注意。

     9.2.完善页面

    有了用户状态,就可以根据用户登录与否,展示不同的页面,比如导航条内容:

    首先,修改base.html文件:

     <div class="collapse navbar-collapse" id="my-nav">
              <ul class="nav navbar-nav">
                <li class="active"><a href="/index/">主页</a></li>
              </ul>
              <ul class="nav navbar-nav navbar-right">
                  {% if request.session.is_login %}
                      <li><a href="#">当前在线:{{ request.session.user_name }}</a></li>
                      <li><a href="/logout/">登出</a></li>
                  {% else %}
                      <li><a href="/login/">登录</a></li>
                      <li><a href="/register/">注册</a></li>
                  {% endif %}
              </ul>
            </div><!-- /.navbar-collapse -->
          </div><!-- /.container-fluid -->

    通过if判断,当登录时,显示当前用户名和登出按钮。未登录时,显示登录和注册按钮。

    注意其中的模板语言,{{ request }}这个变量会被默认传入模板中,可以通过圆点的调用方式,获取它内部的{{ request.session }},再进一步的获取session中的内容。其实{{ request }}中的数据远不止此,例如{{ request.path }}就可以获取先前的url地址。

    再修改一下index.html页面,根据登录与否的不同,显示不同的内容:

    {% extends 'base.html' %}
    {% block title %}主页{% endblock %}
    {% block content %}
        {% if request.session.is_login %}
        <h1>你好,{{ request.session.user_name }}!欢迎回来!</h1>
        {% else %}
        <h1>你尚未登录,只能访问公开内容!</h1>
        {% endif %}
    {% endblock %}

    看下效果:

     十、注册视图

     10.1.创建forms

    /login/forms.py中添加一个新的表单类:

    class RegisterForm(forms.Form):
        gender = (
            ('male', "男"),
            ('female', "女"),
        )
        username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
        password1 = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
        password2 = forms.CharField(label="确认密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
        email = forms.EmailField(label="邮箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
        sex = forms.ChoiceField(label='性别', choices=gender)
        captcha = CaptchaField(label='验证码')

    说明:

    • gender和User模型中的一样,其实可以拉出来作为常量共用,为了直观,特意重写一遍;
    • password1和password2,用于输入两遍密码,并进行比较,防止误输密码;
    • email是一个邮箱输入框;
    • sex是一个select下拉框;

     10.2.完善register.html

    同样地,类似login.html文件,我们在register.html中编写forms相关条目:

    {% extends 'login/base.html' %}
    
    {% block title %}注册{% endblock %}
    {% block content %}
        <div class="container">
            <div class="col-md-4 col-md-offset-4">
              <form class='form-register' action="/register/" method="post">
    
                  {% if message %}
                      <div class="alert alert-warning">{{ message }}</div>
                  {% endif %}
    
                  {% csrf_token %}
    
                  <h2 class="text-center">欢迎注册</h2>
                  <div class="form-group">
                      {{ register_form.username.label_tag }}
                      {{ register_form.username}}
                  </div>
                  <div class="form-group">
                      {{ register_form.password1.label_tag }}
                      {{ register_form.password1 }}
                  </div>
                  <div class="form-group">
                      {{ register_form.password2.label_tag }}
                      {{ register_form.password2 }}
                  </div>
                  <div class="form-group">
                      {{ register_form.email.label_tag }}
                      {{ register_form.email }}
                  </div>
                  <div class="form-group">
                      {{ register_form.sex.label_tag }}
                      {{ register_form.sex }}
                  </div>
                  <div class="form-group">
                      {{ register_form.captcha.errors }}
                      {{ register_form.captcha.label_tag }}
                      {{ register_form.captcha }}
                  </div>
    
                  <button type="reset" class="btn btn-default pull-left">重置</button>
                  <button type="submit" class="btn btn-primary pull-right">提交</button>
    
              </form>
            </div>
        </div> <!-- /container -->
    {% endblock %}

    10.3.注册视图

    进入/login/views.py文件,现在来完善我们的register()视图:

    def register(request):
        if request.session.get('is_login', None):
            # 登录状态不允许注册。你可以修改这条原则!
            return redirect("/index/")
        if request.method == "POST":
            register_form = RegisterForm(request.POST)
            message = "请检查填写的内容!"
            if register_form.is_valid():  # 获取数据
                username = register_form.cleaned_data['username']
                password1 = register_form.cleaned_data['password1']
                password2 = register_form.cleaned_data['password2']
                email = register_form.cleaned_data['email']
                sex = register_form.cleaned_data['sex']
                if password1 != password2:  # 判断两次密码是否相同
                    message = "两次输入的密码不同!"
                    return render(request, 'login/register.html', locals())
                else:
                    same_name_user = models.User.objects.filter(name=username)
                    if same_name_user:  # 用户名唯一
                        message = '用户已经存在,请重新选择用户名!'
                        return render(request, 'login/register.html', locals())
                    same_email_user = models.User.objects.filter(email=email)
                    if same_email_user:  # 邮箱地址唯一
                        message = '该邮箱地址已被注册,请使用别的邮箱!'
                        return render(request, 'login/register.html', locals())
    
                    # 当一切都OK的情况下,创建新用户
    
                    new_user = models.User.objects.create()
                    new_user.name = username
                    new_user.password = password1
                    new_user.email = email
                    new_user.sex = sex
                    new_user.save()
                    return redirect('/login/')  # 自动跳转到登录页面
        register_form = RegisterForm()
        return render(request, 'login/register.html', locals())

    从大体逻辑上,也是先实例化一个RegisterForm的对象,然后使用is_valide()验证数据,再从cleaned_data中获取数据。

    重点在于注册逻辑,首先两次输入的密码必须相同,其次不能存在相同用户名和邮箱,最后如果条件都满足,利用ORM的API,创建一个用户实例,然后保存到数据库内。

    看一下注册的页面:

    注册成功在admin后台可以看到注册的用户

    10.4.密码加密

    用户注册的密码应该加密才对

    对于如何加密密码,有很多不同的途径,其安全程度也高低不等。这里我们使用Python内置的hashlib库,使用哈希值的方式加密密码,可能安全等级不够高,但足够简单,方便使用,不是么?

    首先在login/views.py中编写一个hash函数:

    import hashlib
    
    def hash_code(s, salt='mysite'):# 加点盐
        h = hashlib.sha256()
        s += salt
        h.update(s.encode())  # update方法只接收bytes类型
        return h.hexdigest()

    然后,我们还要对login()和register()视图进行一下修改:

    #login.html
    
    if user.password == hash_code(password):  # 哈希值和数据库内的值进行比对
    
    #register.html
    
    new_user.password = hash_code(password1)  # 使用加密密码
    # login/views.py
    
    from django.shortcuts import render,redirect
    from . import models
    from .forms import UserForm,RegisterForm
    import hashlib
    
    def index(request):
        pass
        return render(request,'login/index.html')
    
    def login(request):
        if request.session.get('is_login', None):
            return redirect("/index/")
        if request.method == "POST":
            login_form = UserForm(request.POST)
            message = "请检查填写的内容!"
            if login_form.is_valid():
                username = login_form.cleaned_data['username']
                password = login_form.cleaned_data['password']
                try:
                    user = models.User.objects.get(name=username)
                    if user.password == hash_code(password):  # 哈希值和数据库内的值进行比对
                        request.session['is_login'] = True
                        request.session['user_id'] = user.id
                        request.session['user_name'] = user.name
                        return redirect('/index/')
                    else:
                        message = "密码不正确!"
                except:
                    message = "用户不存在!"
            return render(request, 'login/login.html', locals())
    
        login_form = UserForm()
        return render(request, 'login/login.html', locals())
    
    
    def register(request):
        if request.session.get('is_login', None):
            # 登录状态不允许注册。你可以修改这条原则!
            return redirect("/index/")
        if request.method == "POST":
            register_form = RegisterForm(request.POST)
            message = "请检查填写的内容!"
            if register_form.is_valid():  # 获取数据
                username = register_form.cleaned_data['username']
                password1 = register_form.cleaned_data['password1']
                password2 = register_form.cleaned_data['password2']
                email = register_form.cleaned_data['email']
                sex = register_form.cleaned_data['sex']
                if password1 != password2:  # 判断两次密码是否相同
                    message = "两次输入的密码不同!"
                    return render(request, 'login/register.html', locals())
                else:
                    same_name_user = models.User.objects.filter(name=username)
                    if same_name_user:  # 用户名唯一
                        message = '用户已经存在,请重新选择用户名!'
                        return render(request, 'login/register.html', locals())
                    same_email_user = models.User.objects.filter(email=email)
                    if same_email_user:  # 邮箱地址唯一
                        message = '该邮箱地址已被注册,请使用别的邮箱!'
                        return render(request, 'login/register.html', locals())
    
                    # 当一切都OK的情况下,创建新用户
    
                    new_user = models.User.objects.create()
                    new_user.name = username
                    new_user.password = hash_code(password1)  # 使用加密密码
                    new_user.email = email
                    new_user.sex = sex
                    new_user.save()
                    return redirect('/login/')  # 自动跳转到登录页面
        register_form = RegisterForm()
        return render(request, 'login/register.html', locals())
    
    def logout(request):
        if not request.session.get('is_login',None):
            return redirect('/index/')
        request.session.flush()
    
        return redirect('/index/')
    
    def hash_code(s, salt='mysite_login'):
        h = hashlib.sha256()
        s += salt
        h.update(s.encode())  # update方法只接收bytes类型
        return h.hexdigest()

    重启服务器,进入注册页面,新建一个用户,然后进入admin后台,查看用户的密码情况:

     再使用该用户登录一下,大功告成!

    可以看到密码长度根据你哈希算法的不同,已经变得很长了,所以前面model中设置password字段时,不要想当然的将max_length设置为16这么小的数字。

    展开全文
  • django sqlite3交互

    2016-04-14 12:03:33
    orm设计理念 object relation map 对象关系映射 数据库的表实体的类一一对应 一对一 一对多 表生成类, 类生成表, orm都可以实现 剥离了select语句 ------------------------------------...from django.db import mo
    orm设计理念
    object relation map
    对象关系映射

    数据库的表实体的类一一对应
    一对一  一对多
    表生成类, 类生成表, orm都可以实现
    剥离了select语句
    -----------------------------------------
    定义类来生成数据库
    打开blog/models.py,新建一个student类

    from django.db import models

    # Create your models here.
    class Student(models.Model):
        name = models.CharField(max_length = 50)
        age = models.IntegerField()
    -----
    打开website/setting.py  修改DATABASES配置

    DATABASES = {
        'default': {
           'ENGINE': 'django.db.backends.sqlite3',
            # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
           'NAME': 'blogdb',
        }
    }
    ----
    命令行
    cd C:\Users\valex\Documents\django web\website
    manage.py syncdb #django 1.7
    manage.py makemigrations  #1.9
    manage.py migrate #1.9

    • migrate: 用于执行迁移动作
    • makemigrations: 基于当前的model创建新的迁移策略文件
    • sqlmigrate: 显示迁移的SQL语句

    这是打开数据库发现已经添加了表blog_student
    -----
    修改类增加intime属性
    ...
    class Student(models.Model):
        name = models.CharField(max_length = 50)
        age = models.IntegerField()
       intime = models.dateField()
    ...
    命令行
    manage.py makemigrations
    manage.py migrate
    然后再sqliteStudio重新连接数据库就可以看见新加的属性

    --------------------------------------------------------------------------------

    修改数据库以更新类模型
    在sqliteStudio中给student 增加字段sex, 并增加新表teacher
    在命令行下执行
    manage.py inspectdb
    manage.py inspectdb > blog/models.py
    修改到models.py

    -----------------------------------------------------------------------------------
    有时候在sqlite中不能同步类模型,先建个空的,再试一下





    展开全文
  • 1-在mysql中建立的数据库,命名为:pythonweb 2-In your settings.py,set your database 1 DATABASES = { 2 'default': { ... 3 'ENGINE': 'django.db.backends.mysql', 4 'NAME': 'pythonweb',...
  • 1.安装 请提前安装好mysql, 另外需要在虚拟环境中安装pymysql: $pip install pymysql 在mysql数据库中创建database空库guazi。 2.设置数据库 在setting.py中找到字典 ... 'ENGINE': 'django.db.backends.mysql', ...
  • 1、创建一个简单视图 这章是按照DgangoBook的说明。在我们创建的工程目录下面DjangoE_1(这是我为自己的...from django.http import HttpResponse def hello(request): return HttpResponse("hello world") ...
  • Django

    万次阅读 多人点赞 2019-09-02 15:36:17
    Django 一,简介 Django是一个开放源代码的Web应用框架,由Python写成。采用了MTV的框架模式,即模型M,视图V和模版T。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理...
  • Django框架实现在线考试系统

    万次阅读 多人点赞 2020-08-27 23:03:44
    遇见Django框架之简单版在线考试系统1.Django的简介2.项目的设计思路3.搭建你的开发环境4.分模块设计5.总结 1.Django的简介 Django是一个基于MVC构造的框架。但是在Django中,控制器接受用户输入的部分由框架自行...
  • (一)Django工作流程介绍

    千次阅读 2019-03-27 11:47:43
    Django作为web框架,想要了解其流程,就必须要了解一下web服务器与web框架之间的关系 请求与响应过程 由客户端发起请求,服务器进行响应,请求与响应过程遵循HTTP协议 web服务器与web框架 当客户端发起请求时,...
  • django项目开发实战——博客

    万次阅读 多人点赞 2018-08-08 17:53:22
    这里用的是python3.7和Django2.0.假设全都安装成功= = 然后这个是我做个人网站的一个过程=。= 然后这个也不能算是个教程吧=。= 然后这只是粗糙的记录=。= 最后,防脱发用霸王=3= 生成一个完整的django项目 ...
  • Django 2.1入门教程(一)

    万次阅读 热门讨论 2018-08-14 15:53:47
    开发环境 Win 7 64位 PyCharm Community 2018.2.1,下载地址:http://www.jetbrains.com/pycharm/download/#section=windows Python 3.6.6,下载地址:... Django 2.1,官网:https...
  • 两种方式查看自己的Django版本

    万次阅读 2017-08-24 09:33:14
    我自己发现了两种方式: 【第一种方式】 Windows系统下 按住Windows按键 + R 进入搜索;搜索CMD进入控制台;输入Python进入Python解释器 Linux系统下 直接使用终端调用Python解释器 ...>>> print(django.VERSIO
  • Django 2.0 官方中文文档终于发布啦! 附链接!

    万次阅读 多人点赞 2020-09-15 23:06:55
    对于很多想从事学习Django Web开发的很多人而言,Django一直没有官方的中文文档是个弊端。而且Django更新较快,国内很多翻译版本都太滞后了。现在小编我告诉大家一个好消息!! Django 2.0 官方中文文档终于发布啦! ...
  • django 修改server端口号

    万次阅读 2017-04-21 15:27:28
    django可以在运行服务器时指定端口号python manage.py runserver 8080
  • Java spring 和python Django的一些对比

    万次阅读 2016-11-06 10:20:14
    http://vschart.com/compare/spring-framework/vs/django-framework
  • Django 框架

    千次阅读 2020-09-10 10:01:06
    Django 框架 特别鸣谢,菜鸟教程的知识赞助 Django 简介 Django 安装 Django 创建第一个项目 Django 模板 Django 模型 Django 表单 Django 视图 Django 路由 Django Admin 管理工具 Django ORM - 单表实例 Django ...
  • django2.x报错No module named 'django.core.urlresolvers'

    万次阅读 多人点赞 2018-01-23 20:18:17
    解决方法就是: from django.urls import reverse 最近从django1.9迁移到django2.0中出现一个意外的报错: ...简单来说,原因就是:django2.0 把原来的 django.core.urlresolvers 包 更改为了 django.urls包,
1 2 3 4 5 ... 20
收藏数 199,108
精华内容 79,643
关键字:

django