django_djangorestframework - 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无疑是Python领域,最热的Web开发框架,功能强大,开发效率高,提供了强大的后台管理和ORM设计。但是发现很多同学都停留在Django的Hello World层面,能做出完整项目的是少之又少。基于这个原因,我开设了...
  • 记录一次完整的基于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入门(一)

    千次阅读 2019-08-01 14:34:09
    跟书《python编程:从入门到实践》,学习用Django编写名为“学习笔记”的Web应用程序。 建立项目 项目路径:learning_log 安装python3.7: 此处省略,参考这里:...

    跟书《python编程:从入门到实践》,学习用Django编写名为“学习笔记”的Web应用程序。

    建立项目

    项目路径:learning_log

    • 安装python3.7:

    此处省略,参考这里:https://blog.csdn.net/miss1181248983/article/details/90779786

    • 建立虚拟环境:
    # cd /opt/
    
    # mkdir learning_log && cd learning_log
    
    # python -m venv 11_env
    
    • 激活虚拟环境:
    # source ll_env/bin/activate
    

    进入虚拟环境终端。

    要停止使用虚拟环境,可执行:

    # deactivate
    
    • 安装Django:
    # pip3 install Django
    

    Django仅在虚拟环境处于活动状态时才可用。

    下面与书中不同部分是由于Django版本更新导致,照着操作即可。本人Django版本为2.2.3,可通过命令python -m django --version查看Django版本。

    • 在Django中创建项目:
    # django-admin.py startproject learning_log .
    
    # ls
    learning_log  ll_env  manage.py
    
    # ls learning_log/
    __init__.py  settings.py  urls.py  wsgi.py
    

    Django新建了一个名为learning_log的目录。它还创建了一个名为manage.py的文件,这是一个简单的程序,它接受命令并将其交给Django的相关部分去运行。

    目录learning_log包含4个文件,其中最重要的是settings.pyurls.pywsgi.py

    文件settings.py指定Django如何与你的系统交互以及如何管理项目。
    文件urls.py告诉Django应创建哪些网页来响应浏览器请求。
    文件wsgi.py帮助Django提供它创建的文件,这个文件名是web server gateway interface(Web服务器网关接口)的首字母缩写。

    • 创建数据库:
    # python manage.py migrate
    

    报错:

    django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
    

    查看系统的sqlite3版本:

    # sqlite3 --version
    3.7.17 2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668
    

    系统自带的sqlite3版本比较低,需要更新版本。

    • 更新sqlite3版本:
    #下载安装
    # cd /software
    
    # wget https://www.sqlite.org/2019/sqlite-autoconf-3270200.tar.gz
    
    # tar -zxf sqlite-autoconf-3270200.tar.gz
    
    # cd sqlite-autoconf-3270200
    
    # ./configure --prefix=/usr/local/
    
    # make && make install
    
    #更换版本
    # find /usr -name sqlite3
    
    /usr/bin/sqlite3
    /usr/lib64/python2.7/sqlite3
    /usr/local/bin/sqlite3
    /usr/python/lib/python3.7/sqlite3
    
    # /usr/local/bin/sqlite3 --version
    3.27.2 2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7
    
    # mv /usr/bin/sqlite3 /usr/bin/sqlite3.bak
    
    # ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3
    
    # sqlite3 --version
    3.27.2 2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7
    
    #路径传递给共享库
    # echo 'export LD_LIBRARY_PATH="/usr/local/lib"' >> ~/.bashrc
    
    # source !$
    
    # cd /opt/learning_log/
    
    # rm -rf /software/sqlite-autoconf-3270200*
    
    • 继续创建数据库:
    # python manage.py migrate
    
    # ls
    db.sqlite3  learning_log  ll_env  manage.py
    
    • 查看项目:

    对于虚拟机环境,建议指定本机IP和端口,否则默认是127.0.0.1:8000,浏览器会无法访问。

    # python manage.py runserver 192.168.30.128:8000
    
    # vim learning_log/settings.py
    ALLOWED_HOSTS = ['192.168.30.128']              #允许主机中,添加本机IP或*(任意主机)
    

    Django启动一个服务器,让你能够查看系统中的项目,了解它们的工作情况。当你在浏览器中输入URL以请求网页时,该Django服务器将进行响应:生成合适的网页,并将其发送给浏览器。

    打开浏览器,输入192.168.30.128:8000访问Django项目,如下图:

    在这里插入图片描述


    创建应用程序

    应用路径:learning_logs

    Django项目由一系列应用程序组成,它们协同工作,让项目成为一个整体。我们暂时只创建一个应用程序,它将完成项目的大部分工作。


    激活虚拟环境

    虚拟机再打开一个终端

    # cd /opt/learning_log/
    
    # source ll_env/bin/activate
    
    # python manage.py startapp learning_logs
    
    # ls
    db.sqlite3  learning_log  learning_logs  ll_env  manage.py
    
    # ls learning_logs/
    admin.py  apps.py  __init__.py  migrations  models.py  tests.py  views.py
    

    上面新增了learning_logs目录,其中models.py用来定义我们要在应用程序中管理的数据。


    定义模型

    # vim learning_logs/models.py
    
    from django.db import models
    
    class Topic(models.Model):
        """用户学习的主题"""
        text = models.CharField(max_length=200)
        date_added = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            """返回模型的字符串表示"""
            return self.text
    

    创建了一个名为Topic的类,它继承了Model——Django中一个定义了模型基本功能的类。Topic类只有两个属性:textdate_added

    属性text是一个CharField——由字符或文本组成的数据,需要存储少量的文本,如名称、标题或城市时,可使用CharField。定义CharField属性时,需要告诉Django该在数据库中预留多少空间,这里设置为200个字符。

    属性date_added是一个DateTimeField——记录日期和时间的数据。传递了实参auto_add_now=True,每当用户创建新主题时,这都让Django将这个属性自动设置成当前日期和时间。


    激活模型

    要使用模型,必须让Django将应用程序包含到项目中。

    # vim learning_log/settings.py
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        # 我的应用程序
        'learning_logs',                #增加该行
    ]
    

    还需要让Django修改数据库,使其能够存储于模型Topic相关的信息。

    # python manage.py makemigrations learning_logs
    
    # python manage.py migrate
    

    在这里,Django确认为learning_logs 应用迁移时一切OK。

    每当需要修改项目管理的数据时,都采取如下三个步骤:

    1. 修改models.py
    2. learning_logs调用makemigrations
    3. 让Django迁移项目。

    Django管理网站

    • 创建超级用户:Django允许创建具备所有权限的用户——超级用户。
    # python manage.py createsuperuser
    
    Username (leave blank to use 'root'): ll_admin              #自定义用户名
    Email address:              #可为空
    Password:               #自定义密码,不小于8位
    Password (again): 
    Superuser created successfully.
    
    • 向管理网站注册模型:非自动创建的模型需要手工注册。
    # vim learning_logs/admin.py
    
    from django.contrib import admin
    
    from learning_logs.models import Topic
    
    admin.site.register(Topic)
    

    导入我们要注册的模型Topic,让Django通过管理网站管理我们的模型。使用超级用户访问管理网站:192.168.30.128:8000/admin/

    在这里插入图片描述

    在这里插入图片描述

    • 添加主题:注册Topic后,添加主题Chess和Rock Climbing。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述


    定义模型Entry

    要记录学到的Chess和Rock Climbing知识,需要为用户可在“学习笔记”中添加的条目定义模型。每个条目都与特定主题相关联,这种关系被称为多对一关系,即多个条目可关联到同一个主题。

    # vim learning_logs/models.py
    
    from django.db import models
    
    class Topic(models.Model):
        """用户学习的主题"""
        text = models.CharField(max_length=200)
        date_added = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            """返回模型的字符串表示"""
            return self.text
    
    class Entry(models.Model):
        """学到的有关某个主题的具体知识"""
        topic = models.ForeignKey(Topic)
        text = models.TextField()
        date_added = models.DateTimeField(auto_now_add=True)
        
        class Meta:
            verbose_name_plural = 'entries'
    
        def __str__(self):
            """返回模型的字符串表示"""
            return self.text[:50] + "..."
    

    像Topic一样,Entry也继承了Django基类Model。属性topic是一个ForeignKey实例,外键引用了数据库中的另一条记录,这些代码将每个条目关联到特定的主题。每个主题创建时,都给它分配了一个键(ID),在两项数据之间需要建立联系时,Django使用与每项信息相关联的键。

    属性text是一个TextField实例,这个字段不需要限制长度。属性date_added能够按创建顺序呈现条目,且在每个条目旁边放置时间戳。

    在Entry类中嵌套Meta类,Meta类存储用于管理模型的额外信息,使用一个特殊属性让Django在需要时使用Entries来表示多个条目;如果没有这个类,Django将使用Entrys来表示多个条目。方法__str__()告诉Django在呈现条目时应显示text的前50个字符,超出则用...显示。


    迁移模型Entry

    # python manage.py makemigrations learning_logs
    

    报错:

    TypeError: __init__() missing 1 required positional argument: 'on_delete'
    

    解决——修改models.py

    from django.db import models
    
    class Topic(models.Model):
        """用户学习的主题"""
        text = models.CharField(max_length=200)
        date_added = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            """返回模型的字符串表示"""
            return self.text
    
    class Entry(models.Model):
        """学到的有关某个主题的具体知识"""
        topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
        text = models.TextField()
        date_added = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            verbose_name_plural = 'entries'
    
        def __str__(self):
            """返回模型的字符串表示"""
            return self.text[:50] + "..."
    

    models.CASCADE这个参数在老版本中是默认值。
    on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值:
    CASCADE:此值设置,是级联删除;
    PROTECT:此值设置,是会报完整性错误;
    SET_NULL:此值设置,会把外键设置为null,前提是允许为null;
    SET_DEFAULT:此值设置,会把设置为外键的默认值;
    SET():此值设置,会调用外面的值,可以是一个函数。

    再次迁移模型Entry:

    # python manage.py makemigrations learning_logs
    
    # python manage.py migrate
    

    向管理网站注册模型Entry

    # vim learning_logs/admin.py 
    
    from django.contrib import admin
    
    from learning_logs.models import Topic, Entry
    
    admin.site.register(Topic)
    admin.site.register(Entry)
    

    查看网页,添加条目

    在这里插入图片描述

    下拉选择对应的Topic,Text添加任意内容

    在这里插入图片描述

    在这里插入图片描述

    继续添加主题Chess的条目,然后再添加主题Rock Climbing的条目

    在这里插入图片描述


    使用Django shell

    Django shell用于测试项目及排除项目故障,要退出shell会话,可按Ctr + D。

    # python manage.py shell
    
    >>> from learning_logs.models import Topic
    
    >>> Topic.objects.all()
    <QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]>
    

    在活动的虚拟环境中执行时,命令python manage.py shell启动一个Python解释器,可使用它来探索存储在项目数据库中的数据。

    在这里,我们导入了模块learning_logs.models中的模型Topic,然后使用方法Topic.objects.all()来获取模型Topic的所有实例;它返回的是一个列表,称为查询集(queryset)。

    • 查看分配给每个主题对象的ID:
    >>> topics = Topic.objects.all()
    
    >>> for topic in topics:
    ...     print(topic.id, topic)
    ... 
    1 Chess
    2 Rock Climbing
    

    可以看到,主题Chess的ID为1,而Rock Climbing的ID为2。

    • 获取任意属性的值:

    知道对象的ID后,就可以获取该对象并查看其任何属性。

    >>> t = Topic.objects.get(id=1)
    
    >>> t.text
    'Chess'
    
    >>> t.date_added
    datetime.datetime(2019, 7, 9, 2, 5, 18, 289746, tzinfo=<UTC>)
    
    • 查看与主题相关联的条目:
    >>> t.entry_set.all()
    <QuerySet [<Entry: The opening is thefirst part ofthe game, roughly t...>, <Entry: In the opening phase ofthe game, it's important to...>]>
    
    >>> t2 = Topic.objects.get(id=2)
    
    >>> t2.entry_set.all()
    <QuerySet [<Entry: One ofthe most importantconcepts in climbing is to...>]>
    

    创建主页网页

    使用Django创建网页的过程通常分三个阶段:定义URL、编写视图和编写模板。

    首先,你必须定义URL模式。URL模式描述了URL是如何设计的,让Django知道如何将浏览器请求与网站URL匹配,以确定返回哪个网页。每个URL都被映射到特定的视图——视图函数获取并处理网页所需的数据。视图函数通常调用一个模板,后者生成浏览器能够理解的网页。


    映射URL

    用户通过在浏览器中输入URL以及单击链接来请求网页,因此我们需要确定项目需要哪些URL。主页的URL最重要,它是用户用来访问项目的基础URL。

    # vim learning_log/urls.py
    
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include('learning_logs.urls', namespace='learning_logs')),
    ]
    

    这里保存完文件之后会报ModuleNotFoundError: No module named 'learning_logs.urls'错误,不用理会,继续往下做。

    在文件夹learning_logs中创建另一个urls.py文件

    # vim learning_logs/urls.py
    
    """定义learning_logs的URL模式"""
    
    from django.urls import path
    from . import views
    
    app_name='learning_logs'
    urlpatterns = [
        # 主页
        path('', views.index, name='index'),
    ]
    

    编写视图

    视图函数接受请求中的信息,准备好生成网页所需的数据,再将这些数据发送给浏览器——这通常是使用定义了网页是什么样的模板实现的。

    # vim learning_logs/views.py
    
    from django.shortcuts import render
    
    from .models import Topic
    
    def index(request):
        """学习笔记的主页"""
        return render(request, 'learning_logs/index.html')
    

    导入了函数render(),它根据视图提供的数据渲染响应。URL请求与刚才定义的模式匹配时,Django将在文件views.py中查找函数index(),再将请求对象传递给这个视图函数。

    • 编写模板:

    模板定义了网页的结构。模板指定了网页是什么样的,而每当网页被请求时,Django将填入相关的数据。模板让你能够访问视图提供的任何数据。

    # mkdir -p learning_logs/templates/learning_logs
    
    # vim learning_logs/templates/learning_logs/index.html
    
    <p>Learning Log</p>
    <p>Learning Log helps you keep track of your learning, for any topic you're learning about.</p>
    

    标签<p></p>标识段落;标签<p>指出了段落的开头位置,而标签</p>指出了段落的结束位置。

    • 访问网页:

    访问基础URL——192.168.30.128:8000,可以看到刚刚定义的模板index.html。

    在这里插入图片描述


    创建其它网页

    在创建完主页之后,继续创建两个显示数据的网页,其中一个列出所有的主题,另一个显示特定主题的所有条目。对于每个网页,我们都将指定URL模式,编写一个视图函数,并编写一个模板。


    模板继承

    创建网站时,几乎都有一些所有网页都将包含的元素。在这种情况下,可编写一个包含通用元素的父模板,并让每个网页都继承这个模板。

    • 父模板:首先来创建一个名为base.html的模板,并将其存储在index.html所在的目录中。这个文件包含所有页面都有的元素;其他的模板都继承base.html。
    # vim learning_logs/templates/learning_logs/base.html
    
    <p>
      <a href="{% url 'learning_logs:index' %}">Learning Log</a>
    </p>
    
    {% block content %}{% endblock content %}
    

    第一部分创建一个包含项目名的段落,该段落也是一个到主页的链接,为创建链接,我们使用了一个模板标签。模板标签{% url 'learning_logs:index' %}生成一个URL,该URL与learning_logs/urls.py中定义的名为index的URL模式匹配。

    第二部分插入了一对块标签。这个块名为content,是一个占位符,其中包含的信息将由子模板指定。

    • 子模板:现在要重新编写index.html,使其继承base.html。
    # vim learning_logs/templates/learning_logs/index.html
    
    {% extends "learning_logs/base.html" %}
    
    {% block content %}
      <p>Learning Log helps you keep track of your learning, for any topic you're learning about.</p>
    {% endblock content %}
    

    子模板的第一行必须包含标签{% extends %},让Django知道它继承了哪个父模板。

    然后插入了一个名为content 的{% block %}标签,以定义content块,不是从父模板继承的内容都包含在content块中。


    显示所有主题

    • URL模式:

    定义显示所有主题的页面的URL。

    # vim learning_logs/urls.py
    
    """定义learning_logs的URL模式"""
    
    from django.urls import path
    from . import views
    
    app_name='learning_logs'
    urlpatterns = [
        # 主页
        path('', views.index, name='index'),
    
        # 显示所有的主题
        path('topics/', views.topics, name='topics'),
    ]
    

    只在用于主页URL的正则表达式中添加了topics/,Django检查请求的URL时,这个模式与这样的URL匹配:基础URL后面跟着topics。其URL与该模式匹配的请求都将交给views.py中的函数topics()进行处理。

    • 修改视图:

    函数topics()需要从数据库中获取一些数据,并将其发送给模板。

    # vim learning_logs/views.py
    
    from django.shortcuts import render
    
    from .models import Topic
    
    def index(request):
        """学习笔记的主页"""
        return render(request, 'learning_logs/index.html')
    
    def topics(request):
        """显示所有主题"""
        topics = Topic.objects.order_by('date_added')
        context = {'topics': topics}
        return render(request, 'learning_logs/topics.html', context)
    

    函数topics() 包含一个形参:Django从服务器那里收到的request对象。然后查询数据库——请求提供Topic对象,并按属性date_added对它们进行排序。我们将返回的查询集存储在topics中。

    接着定义了一个将要发送给模板的上下文。上下文是一个字典,其中的键是我们将在模板中用来访问数据的名称,而值是我们要发送给模板的数据。在这里,只有一个键—值对,它包含我们将在网页中显示的一组主题。创建使用数据的网页时,除对象request和模板的路径外,我们还将变量context传递给render()

    • 编写模板:

    显示所有主题的页面的模板接受字典context,以便能够使用topics()提供的数据。

    # vim learning_logs/templates/learning_logs/topics.html
    
    {% extends "learning_logs/base.html" %}
    
    {% block content %}
    
      <p>Topics</p>
      
      <ul>
        {% for topic in topics %}
          <li>{{ topic }}</li>
        {% empty %}
          <li>No topics have been added yet.</li>
        {% endfor %}
      </ul>
      
    {% endblock content %}
    

    首先使用标签{% extends %}来继承base.html,接着开始定义content块。在标准HTML中,项目列表被称为无序列表,用标签<ul></ul>表示。

    然后使用一个相当于for循环的模板标签,它遍历字典context中的列表topics。在模板中,每个for循环都必须使用{% endfor %}标签来显式地指出其结束位置。

    在循环中,要将每个主题转换为一个项目列表项。要在模板中打印变量,需要将变量名用双花括号括起来。HTML标签<li></li>表示一个项目列表项,在标签对<ul></ul>内部,位于标签<li></li>之间的内容都是一个项目列表项。

    修改父模板,使其包含到显示所有主题的页面的链接:

    # vim learning_logs/templates/learning_logs/base.html
    
    <p>
      <a href="{% url 'learning_logs:index' %}">Learning Log</a> -
      <a href="{% url 'learning_logs:topics' %}">Topics</a>
    </p>
    
    {% block content %}{% endblock content %}
    

    在到主页的链接后面添加了一个连字符-,然后添加了一个到显示所有主题的页面的链接,使用的也是模板标签url。

    • 访问网页:

    访问基础URL——192.168.30.128:8000,可以看到定义的模板index.html及Topics链接。

    在这里插入图片描述

    点击Topics链接,查看主题

    在这里插入图片描述


    显示特定主题页面

    接下来,重复之前步骤,创建一个专注于特定主题的页面——显示该主题的名称及该主题的所有条目。同样的,我们将定义一个新的URL模式,编写一个视图并创建一个模板。

    • URL模式:

    显示特定主题的页面的URL模式与前面的所有URL模式都稍有不同,因为它将使用主题的id属性来指出请求的是哪个主题。

    # vim learning_logs/urls.py
    
    """定义learning_logs的URL模式"""
    
    from django.urls import path, re_path
    from . import views
    
    app_name='learning_logs'
    urlpatterns = [
        # 主页
        path('', views.index, name='index'),
    
        # 显示所有的主题
        path('topics/', views.topics, name='topics'),
    
        # 特定主题的详细页面
        re_path(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
    ]
    

    这里用到了python正则表达式,/(?P<topic_id>\d+)/与包含在两个斜杠内的整数匹配,并将这个整数存储在一个名为topic_id的实参中。?P<topic_id>将匹配的值存储到topic_id中;而表达式\d+与包含在两个斜杆内的任何数字都匹配,不管这个数字为多少位。

    发现URL与这个模式匹配时,Django将调用视图函数topic(),并将存储在topic_id中的值作为实参传递给它。

    • 修改视图:
    # vim learning_logs/views.py
    
    from django.shortcuts import render
    
    from .models import Topic
    
    def index(request):
        """学习笔记的主页"""
        return render(request, 'learning_logs/index.html')
    
    def topics(request):
        """显示所有主题"""
        topics = Topic.objects.order_by('date_added')
        context = {'topics': topics}
        return render(request, 'learning_logs/topics.html', context)
    
    def topic(request, topic_id):
        """显示一个主题及其详细页面"""
        topic = Topic.objects.get(id=topic_id)
        entries = topic.entry_set.order_by('-date_added')
        context = {'topic': topic, 'entries': entries}
        return render(request, 'learning_logs/topic.html', context)
    

    第一个除request对象外还包含另一个形参的视图函数。这个函数接受正则表达式(?P<topic_id>\d+)捕获的值,并将其存储到topic_id中。

    使用get()来获取指定的主题,获取与该主题相关联的条目,并将它们按date_added排序:date_added前面的减号指定按降序排列,即先显示最近的条目。将主题和条目都存储在字典context中,再将这个字典发送给模板topic.html。

    • 编写模板:
    # vim learning_logs/templates/learning_logs/topic.html
    
    {% extends 'learning_logs/base.html' %}
    
    {% block content %}
    
      <p>Topic: {{ topic }}</p>
    
      <p>Entries:</p>
      <ul>
      {% for entry in entries %}
        <li>
          <p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
          <p>{{ entry.text|linebreaks }}</p>
        </li>
      {% empty %}
        <li>
          There are no entries for this topic yet.
        </li>
      {% endfor %}
      </ul>
    
    {% endblock content %}
    
    • 修改topics模板:

    将显示示所有主题的页面中的每个主题都设置为链接

    # vim learning_logs/templates/learning_logs/topics.html
    
    {% extends "learning_logs/base.html" %}
    
    {% block content %}
    
      <p>Topics</p>
    
      <ul>
        {% for topic in topics %}
          <li>
            <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
    	  </li>
        {% empty %}
          <li>No topics have been added yet.</li>
        {% endfor %}
      </ul>
    
    {% endblock content %}
    

    使用模板标签url 根据learning_logs中名为topic的URL模式来生成合适的链接。这个URL模式要求提供实参topic_id,因此在模板标签url中添加了属性topic.id

    • 访问网页:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述


    展开全文
  • django博客

    2020-07-30 23:32:07
    django,mysql,博客项目,完整的,可用的项目! django,mysql,博客项目,完整的,可用的项目! django,mysql,博客项目,完整的,可用的项目!
  • Django项目实例精讲

    万次阅读 2019-05-21 16:40:20
    创建隔离的python环境 pip install virtualenv #安装 virtualenv my_env#创建目录 which python3 #找到python3目录 virtualenv my_env -p /Library/Frameworks/Python.framework/Versions/3.7/bin/python3 ...
  • 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-05-22 14:22:19
    django2.0搭建个人博客 博客网站的需求与规划 该个人博客为简单的入门示范网站,具有以下功能 项目名称为mblog 通过admin管理界面发帖、编辑以及删除贴文,且此界面支持markdown语句 使用BootStrap框架 在主页中...
  • (一)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项目 ...
  • 错误 ImportError: No module named 'django'

    万次阅读 2018-05-21 22:25:48
    原因:可能电脑上并没有安装django在cmd命令行输入python进入pyhton的交互模式后,输入import django再输入django.VERSION表示电脑上有django
  • 两种方式查看自己的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
  • 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包,
  • Django卸载重新安装

    万次阅读 2018-11-08 16:56:26
    不同的项目可能使用了不同的Django版本,兼任性是大问题, 如果不幸要去接手不同版本的项目,比较惨烈! 如果想重装一个Django版本,需要先卸载后安装。 首先找到django安装路径 python import sys; sys.path = ...
1 2 3 4 5 ... 20
收藏数 199,237
精华内容 79,694
关键字:

django