GVKun编程网logo

golang web编程之——读取csdn最新文章(golang读取文件)

19

本篇文章给大家谈谈golangweb编程之——读取csdn最新文章,以及golang读取文件的知识点,同时本文还将给你拓展Cocos2d-x——读取Csv配置文件、CSDN文章列表的CSS实现_htm

本篇文章给大家谈谈golang web编程之——读取csdn最新文章,以及golang读取文件的知识点,同时本文还将给你拓展Cocos2d-x——读取Csv配置文件、CSDN文章列表的CSS实现_html/css_WEB-ITnose、Django web编程1 -- 创建项目和应用、Django web编程3 -- 创建用户账户等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

golang web编程之——读取csdn最新文章(golang读取文件)

golang web编程之——读取csdn最新文章(golang读取文件)

利用go语言内置的各种网络包可以方便的进行web编程。本文章利用了csdn的开放API实现读取最新文章的需求。演示了go语言发起http get请求和json的umarshing特性。

原文:小宇的博客

简单的http GET请求

go语言内置了net/http包,采用http.Get能够方便的发起GET请求

package main

import (
    "fmt"
    "io/IoUtil"
    "log"
    "net/http"
)

func main() {

    resp,err := http.Get("http://api.csdn.net/blog/getnewarticlelist?client_id=????&page=1&size=10")
    if err != nil {
        log.Fatal(err)
    }
    contents,err := IoUtil.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s",contents)
}

看到返回的内容为json格式。

{"page":1,"count":12733,"size":10,"list":[{"ArticleId":52963517,"BlogId":5022393,"UserName":"u013410747","Title":"linux修改默认的编辑器","Description":"sudo select-editor 选择vim 搞定。。","PostTime":"\/Date(1477712141000)\/","UpdateTime":"\/Date(1477712177908)\/","Digg":0,"Bury":0,"ChannelId":2,"Type":1,"Status":0,"ViewCount":0,"CommentCount":0,"CommentAuth":2,"IsTop":false,"Level":0,"OutlinkCount":0,"Note":null,"IP":null,"Categories":null,"Tags":[],"ColumnAlias":null,"ColumnTitle":null,"MarkDownContent":null,"MarkDownDirectory":null,"ArticleEditType":0,"ArticleMore":null},...umnAlias":null,"ColumnTitle":null,"MarkDownContent":null,"MarkDownDirectory":null,"ArticleEditType":0,"ArticleMore":null}]}成功: 进程退出代码 0. 

JSON转为内部结构体

那么如何把json格式的内容转为golang的内部结构体呢?这需要利用json.Unmarshal

package main

import (
    "encoding/json"
    "fmt"
    "io/IoUtil"
    "log"
    "net/http"
)

type Article struct {
    ArticleId   int
    BlogId      int
    UserName    string
    Title       string
    Description string
    PostTime    string
    UpdateTime  string
    ViewCount    int
    CommentCount int
    Categories  int
    ColumnAlias bool
    Url         string
}
type Articles struct {
    Page  int `json:"page,int"`
    Count int
    Size  int
    List  []*Article
}

func main() {
    resp,err := http.Get("http://api.csdn.net/blog/getnewarticlelist?client_id=????&page=0&size=20")
    if err != nil {
        log.Fatal(err)
    }
    str,err := IoUtil.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil {
        log.Fatal(err)
    }

    var contents Articles
    if err := json.Unmarshal(str,&contents); err != nil {
        log.Fatal("Json unmarshing @R_301_4753@: ",err)
    }

    for k,v := range contents.List {
        fmt.Printf("%v. %v\n",k,v.Title)
    }
}

最后,我们完成了用csdn的开放API实现读取最新文章的需求。

0. 1048. Find Coins (25)解题报告
1. 1049. Counting Ones (30)解题报告
2. java实习第二天
3. 1060. 爱丁顿数(25)
4. 剑指offer-数组中出现次数超过一半的数字
5. jsonp跨域访问案例
6. framework jar包MAKEFILE示例
7. Unity3d 乱序之惑
8. CodeForces 445C DZY loves Physics
9. LeetCode #424: Longest Repeating Character Replacement
10. 面板显示private变量用标签[Serializefield]
11. Postgresql问题解决--连接数过多
12. 多维高斯分布及多维条件高斯分布
13. 王朝  都要学C
14. CharacterController.Move 实现角色移动
15. bootargs
16. 不用输入法输自己的名字!!!!
17. mybatis mbg自动生成的selectByExample按条件查询不出来值。
18. 系统乔迁留念贴
19. java中Proxy(代理与动态代理)

Cocos2d-x——读取Csv配置文件

Cocos2d-x——读取Csv配置文件

KeyWord : Csv、CCFileUtils


在一个游戏中,通常会有很多怪物、关卡、技能等数据,这些数据不可能在代码里写死,一般都会使用配置文件保存,使用时再加在到

内存。

这次,要给大家介绍在项目里常常使用的一个配置文件——Csv格子文件。


Csv文件其实很简单,它就是一组只用逗号分隔开的字符串,如图:


看起来像是excel文件一样,但是当我们用记事本打开Monster.csv时,可以看到:


我们看到,每一行的字符串都是用逗号分隔,除此之外没有任何多余的格式。

Csv格式的文件除了文件占用空间小之外,还有一个很重要的优点:方便程序解析。


创建一个Csv文件

首先,用微软的office办公软件新建一个Excel文件(或者WPS文档),命名为Monster.xls (Monster.et),然后打开创建的文件,单击菜单 [文件] -> [另存为] ,然后

选择Csv格式文件,如图:

编写字符串工具类——分离字符串

我们创建一个项目,再新建一个类,命名为StringUtil,先看下这个类的头文件:

StringUtil.h:

该类只有一个功能,就是拆分字符串。再来看看StringUtil的实现:

StringUtil.cpp:




我们来测试一下,看看StringUtil是否正常地工作,修改HelloWorldScene的init函数,代
码如下:


调试模式下运行项目,可看到日志输出:


可见,StringUtil正确地拆分了字符串。

接下来,我们马上开始解析Csv文件。

辅助工具——编写文本读写工具类

由于我们的配置文件一行就是一条数据,比如Monster.csv文件,第一行是怪物笨木头的数据,第二行是怪物旁白小若的数据。

因此,我们还需要再写一个类,这个类很简单,负责把一个文件按行加载,保存到CCArray里。

新建一个类,命名为FileLoadUtil,先看头文件:

依旧是一个单例类,只有一个函数,也就是我们刚刚说的,将文件按行存放到一个列表中。再来看看函数实现:

FileLoadUtil.cpp:


CCFileUtils是Cocos2d-x提供的用于文件操作的工具类,使用CCFileUtils的getFileData函数可以

读取 文件并保存为字符对象,然后再把 字符用CCString包装起来,得到一个CCString对象,最后就可

以使 用我们的StringUtil的split函数了。通过换行符将文本按行分隔,保存 到列表里。

getDataLines函数最终返回的列表如下:



编写Csv文件读写工具类

好了,我们要真正开始编写Csv文件读取工具类了。

新建一个类,命名为CsvUtil,来看看头文件:




这个类看起来有点复杂,我们先来看loadFile函数的实现:


loadFile做了三件事情:
(1)将csv文件按行读取保存到列表中,如:

(2)将第一步中的每一行字符串按逗号分隔,保存到列表里,如:

(3)用一个CCDictionary对象保存csvStrList和文件路径sPath的对应关系




我们再来看看第二个重要的函数,代码如下:


getFileRowColNum函数用于获取Csv文件的行和列的数量,由于我们是按行存放文件数据的,所以列

表的大小就是文件行的数量,而每一行又存放一组数据,因此,某行的一组数据列表的大小,就是文

件列的数量。

总结一下:loadFile函数存放文件数据的逻辑

(1)有一个CCDictionary类型的mCsvDict变量,存放一个csvStrList列表和Csv文件名的对应关系:
mCsvDict->setobject(csvStrList,sPath);

(2)csvStrList列表为CCArray类型,存放Csv文件每一行的数据,每一行的数据又由一个tArr列表
保存;

(3)tArr列表保存的是一个个的CCString对象,如ID Name Level HP MP HappyValue。


反过来,加载了Csv文件之后,读取文件数据的情况就是这样:

(1)根据文件名sPath从mCsvDict字典中获取一个csvStrList列表;
(2)csvStrList列表保存了Csv文件每一行的数据;
(3)如果要获取Csv文件第一行的数据,则取得csvStrList列表的第一行,取出来的值又是一个 列表tArr;
(4)tArr里保存了第一行的所有数据,这些数据以CCString类型保存

最后,再解释一个很重要的函数,代码如下:

CSDN文章列表的CSS实现_html/css_WEB-ITnose

CSDN文章列表的CSS实现_html/css_WEB-ITnose

csdn文章的列表视图如下:


立即学习“前端免费学习笔记(深入)”;

看起来,还是挺好看的,本文用CSS实现该效果,实现后得效果如下:


立即学习“前端免费学习笔记(深入)”;


立即学习“前端免费学习笔记(深入)”;


立即学习“前端免费学习笔记(深入)”;

直接贴代码:

    <meta charset="utf-8"><title>Table</title><style>        html,body,table{            font-size: 12px;            font-family: "宋体";            text-align: center;            MARGIN-RIGHT: auto;            MARGIN-LEFT: auto;        }        .top{            background: #48525e;            height: 30px;            color: #fff;            padding-right: 20px;            padding-top: 13px;            font-family: Arial Unicode MS, Arial, sans-serif;        }        .top_links{            text-align: right;            font-size: 12px;            width: 800px;            MARGIN-RIGHT: auto;            MARGIN-LEFT: auto;        }        a{            color: #fff;            text-decoration: none;        }        a:hover{            cursor: pointer;            font-weight: bold;        }        .top_logo{            float: left;            font-weight: bold;            font-size: 15px;            margin-left: 10px;        }        .box{            margin:10px auto 10px auto;            border: 1px solid #bfbfbf;            width: 800px;            text-align: center;            font-size: 100pt;            color:#3B5998 ;            padding: 30px 0px;            font-family: Cambria, Cochin, Georgia, Times, "Times New Roman", serif;        }        table{            width: 800px;            border-collapse: collapse;            border: none;            color: #3B5998;            text-align: center;        }        tr{            border: none;            border-top: 1px solid #bfbfbf;        }        tr:hover{            background: #FFFFCC;        }        .trTitle{            font-weight: bold;            color:#000;            border: none;            border-bottom: 2px solid #bfbfbf;        }        .trTitle:hover{            background: #fff;        }        .altitem{            background: #eee;        }        .tdleft{            text-align: left;        }        .tdleft:hover{            cursor: pointer;            text-decoration: underline;        }        span{            color: #000;        }        .page_nav{            padding-top: 8px;            width: 800px;            MARGIN-RIGHT: auto;            MARGIN-LEFT: auto;            text-align: center;        }        .page_num{            border: 1px solid #dbe5ee;            padding: 3px 8px;            border-radius:3px;        }        .page_num:hover{            cursor: pointer;            text-decoration: underline;        }        .page_num_active{            background: #07519a;            color: #fff;        }    </style><div>    <div>        <div>            <div><a href="www.csdn.com">CSDN</a></div>            <a>首页</a> | <a>我的博客</a> | <a>联系我们</a>        </div>    </div>    <div>        CSDN    </div>    <table cellpadding="9"><tbody>
<tr>
<td>标题</td>                <td>状态</td>                <td>浏览</td>                <td>评价</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
<tr>
<td>Arcgis for Js之鼠标经过显示对象名的实现(2014-12-12 09:00)</td>                <td> </td>                <td>12</td>                <td>23</td>            </tr>
</tbody></table>
<div>        <span>76条数据 共4页</span>        <span>            <span>首页</span>            <span>上一页</span>            <span>1</span>            <span>2</span>            <span>3</span>            <span>下一页</span>            <span>尾页</span>        </span>    </div>
</div>
登录后复制

有需要的可以直接去用,如有疑问联系:

QQ:1004740957

Django web编程1 -- 创建项目和应用

Django web编程1 -- 创建项目和应用

python:3.7.2

Django:2.1.7

1、创建虚拟环境

虚拟环境是系统的一个位置,可以在其中安装包,并将其与其他python包隔离。

创建目录,命名为learning_log,并切换到这个目录下,并创建一个虚拟环境。

$ mkdir learning_log
$ cd learning_log/
learning_log$ python3 -m venv ll_env

2、激活虚拟环境并安装Django

learning_log$ source ll_env/bin/activate
(ll_env)learning_log$ pip3 install Django

3、新建项目

在处于活动的虚拟环境的情况下(ll_env包含在括号内,以下的步骤都是在虚拟环境下),执行如下命令来新建项目:

(ll_env)learning_log$ django-admin.py startproject learning_log .  # 创建项目,点号是让新项目使用合适的目录结构,不能缺少。
(ll_env)learning_log$ ls   # Django新建了一个名为learning_log的目录。它还创建了一个名为manage.py的文件
learning_log ll_env manage.py
(ll_env)learning_log$ ls learning_log  # learning_log包含4个文件
__init__.py settings.py urls.py wsgi.py

settings.py:指定Django如何与你的系统交互以及如何管理项目。在开发项目的过程中,我们将修改其中一些设置,并添加一些设置。

urls.py:告诉Django应创建哪些网页来响应浏览器请求。

wsgi.py:帮助Django提供它创建的文件,这个文件名是web server gateway interface(Web服务器网关接口 )的首字母缩写。

4、创建数据库

(ll_env)learning_log$ python3 manage.py migrate
Operations to perform:...

将修改数据库称为迁移数据库。首次执行命令migrate 时,将让Django确保数据库与项目的当前状态匹配。Django将新建一个数据库。Django指出它将创建必要的数据库表,用于存储我们将在这个项目(Synchronize unmigrated apps,同步未迁移的应用程序 )中使用的 信息,再确保数据库结构与当前代码(Applyallmigrations,应用所有的迁移 )匹配。

(ll_env)learning_log$ ls
db.sqlite3 learning_log ll_env manage.py

我们运行了命令ls ,其输出表明Django又创建了一个文件——db.sqlite3。SQLite是一种使用单个文件的数据库,是编写简单应用程序的理想选择,因为它让你不用太关 注数据库管理的问题。

5、查看项目

(ll_env)learning_log$ python3 manage.py runserver 0.0.0.0:405   # 允许所有IP地址访问,使用405端口
Performing system checks...

System check identified no issues (0 silenced).    # 检查确认正确地创建了项目
March 06, 2019 - 01:45:25
Django version 2.1.7, using settings ''learning_log.settings''   # Django版本以及设置文件的名称
Starting development server at http://0.0.0.0:405/   # 访问地址和端口,如果出现端口占用,则尝试其他端口
Quit the server with CONTROL-C.

6、创建应用程序

新开另一个终端窗口,并切换到manage.py所在的目录,激活该虚拟环境,并执行命令startapp。

learning_log$ source ll_env/bin/activate
(ll_env)learning_log$ python3 manage.py startapp learning_logs   # 创建应用程序
(ll_env)learning_log$ ls    #  新增了文件夹learning_logs
db.sqlite3 learning_log learning_logs ll_env manage.py 
(ll_env)learning_log$ ls learning_logs/     # 最重要的文件是models.py、admin.py和views.py
admin.py __init__.py migrations models.py tests.py views.py

 7、定义模型

模型models.py(在learning_logs目录下面)用于定义我们要在应用程序中管理的数据。在代码层面,模型就是一个类,就像前面讨论的每个类一样,包含属性 和方法。下面是表示用户将要存储的主题的模型:

from django.db import models

class Topic(models.Model):    # 创建Topic主题的类
    """用户学习的主题"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):    # 默认应使用哪个属性来显示有关主题的信息,如果是python2.7,应调用__unicode__()
        """返回模型的字符串表示"""
        return self.text
  • text属性:CharField——由字符或文本组成的数据,适用于需要存储少量的文本,如名称、标题或城市。定义CharField 属性时,必须告诉Django该在数据库中预留多少空间。在这里,我们将max_length 设置成了200(即200个字符)
  • date_added 是一个DateTimeField ——记录日期和时间的数据。我们传递了实参auto_add_now=True ,每当用户创建新主题时,这都让Django将这个属性自动设置成当前日期和时间。
  • __str__():默认应使用哪个属性来显示有关主题的信息。这里返回的是text中的字符串。

其他的属性可以参考:https://docs.djangoproject.com/en/1.8/ref/models/fields/ 。

8、激活模型

需要在learning_log文件夹修改settings.py文件,告诉Django哪些应用程序安装到learning_log项目中。

请将INSTALLED_APPS(这是个元组) 修改成下面这样,将前面的应用程序名称添加到这个元组中:

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

    # 我的应用程序
    ''learning_logs'',
]

接下来,需要让Django修改数据库,使其能够存储与模型Topic 相关的信息。为此,在终端窗口中执行下面的命令:

(ll_env)learning_log$ python3 manage.py makemigrations learning_logs 
Migrations for ''learning_logs'': 0001_initial.py: - Create model Topic

命令makemigrations 让Django确定该如何修改数据库,使其能够存储与我们定义的新模型相关联的数据。输出表明Django创建了一个名为0001_initial.py的迁移文件,这个文件将在数据库中为模型Topic 创建一个表。
下面来应用这种迁移,让Django替我们修改数据库:

(ll_env)learning_log$ python3 manage.py migrate
--snip--
Running migrations:
Rendering model states... DONE
Applying learning_logs.0001_initial... OK    # 应用迁移正常

每当需要修改“学习笔记”管理的数据时,都采取如下三个步骤:

  • 修改models.py;
  • 对learning_logs 调用makemigrations ;
  • 让Django迁移项目。

9、Django管理网站

(1)创建超级用户:创建具备所有权限的用户。

(ll_env)learning_log$ python3 manage.py createsuperuser

(2)向管理网站注册模型:我们创建应用程序learning_logs 时,Django在models.py所在的目录中创建了一个名为admin.py的文件。为向管理网站注册Topic ,请在admin.py输入下面的代码:

from django.contrib import admin

from learning_logs.models import Topic
admin.site.register(Topic)

现在,使用超级用户账户访问管理网站:访问http://localhost:8000/admin/ ,并输入你刚创建的超级用户的用户名和密码,你将看到类似于下图所示的屏幕。这个网页让你能够添加和修改用户和用户组,还可以管理与刚才定义的模型Topic 相关的数据。

(3)添加主题:向管理网站注册Topic 后,我们来添加第一个主题。为此,单击Topics进入主题网页,它几乎是空的,这是因为我们还没有添加任何主题。单击Add,你将看到一个用于添加新主题的表单。在第一个方框中输入Chess ,再单击Save,这将返回到主题管理页面,其中包含刚创建的主题。

10、定义模型Entry

修改models.py中的代码:

# Create your models here.


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):  #  继承了Django基类Model 
    """学到的有关某个主题的具体指示"""

    topic = models.ForeignKey(Topic,on_delete=models.CASCADE)  # 外键。每个主题创建时,都给它分配了一个键(或ID).  
    text = models.TextField()    # 不需要长度限制
    date_added = models.DateTimeField(auto_now_add=True)

    class Meta:  # 用于管理模型的额外信息,在需要时使用Entries来表示多个条目。若没有,将使用Entrys来表示多个条目。
        verbose_name_plural = ''entries''

    def __str__(self):    # 只呈现text前50个字符。
        """返回模型的字符串表示"""
        return self.text[:50] + "..."

11、迁移模型Entry

由于我们添加了一个新模型,因此需要再次迁移数据库。

将慢慢地对这个过程了如指掌:

  • 修改models.py
  • 执行命令python manage.py makemigrations app_name
  • 再执行命令python manage.py migrate。
(ll_env)learning_log$ python3 manage.py makemigrations learning_logs
Migrations for ''learning_logs'':
  learning_logs/migrations/0002_entry.py  # 生成了一个新的迁移文件0002_...
    - Create model Entry
(ll_env)learning_log$ python3 manage.py migrate

12、向管理网站注册Entry

我们还需要注册模型Entry 。为此,需要将admin.py修改成类似于下面这样:

from django.contrib import admin

from learning_logs.models import Topic, Entry

admin.site.register(Topic)
admin.site.register(Entry)

返回到http://localhost:405/admin/learning_logs/ ,你将看到learning_logs下列出了Entries:

单击Entries的Add链接,或者单击Entries再选择Add entry。你将看到一个下拉列表,让你能够选择要为哪个主题创 建条目,还有一个用于输入条目的文本框。从下拉列表中选择Chess,并添加一个条目(略)。

13、Django shell

输入一些数据后,就可通过交互式终端会话以编程方式查看这些数据了。命令python manage.py shell启动一个Python解释器,可使用它来探索存储在项目数据库中的数据。

(ll_env)learning_log$ python3 manage.py shell
>>> from learning_logs.models import Topic
>>> Topic.objects.all()
<QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]>
>>> topics = Topic.objects.all()
>>> for topic in topics:
...     print(topic.id, topic)
... 
1 Chess
2 Rock Climbing
>>> t = Topic.objects.get(id=1)
>>> t.text
''Chess''>>> t.date_added
datetime.datetime(2019, 3, 7, 2, 14, 27, 770273, tzinfo=<UTC>)
>>> t.entry_set.all()
<QuerySet [<Entry: The opening is the first part of the game, roughly...>, <Entry: In the opening phase of the game, it''s important t...>]>

14、创建网页

使用Django创建网页的过程通常分三个阶段:定义URL、编写视图和编写模板。首先,你必须定义URL模式。URL模式描述了URL是如何设计的,让Django知道如何将浏览器请求 与网站URL匹配,以确定返回哪个网页。

每个URL都被映射到特定的视图 ——视图函数获取并处理网页所需的数据。视图函数通常调用一个模板,后者生成浏览器能够理解的网页。为明白其中的工作原理,我们来创建 学习笔记的主页。我们将定义该主页的URL、编写其视图函数并创建一个简单的模板。

(1)映射URL

用户通过在浏览器中输入URL以及单击链接来请求网页,因此我们需要确定项目需要哪些URL。主页的URL最重要,它是用户用来访问项目的基础URL。当前,基础
URL(http://localhost:8000/)返回默认的Django网站,让我们知道正确地建立了项目。我们将修改这一点,将这个基础URL映射到“学习笔记”的主页。

打开项目主文件夹learning_log中的文件urls.py,我们需要在原来的基础上包含learning_logs的URL:

from django.contrib import admin
from django.conf.urls import url, include

urlpatterns = [
    url(r''^admin/'', admin.site.urls),
    url(r'''', include(''learning_logs.urls'',namespace=''learning_logs'')),  # 包含earning_logs.urls内容
]

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

"""定义learning_logs的URL模式"""

from django.conf.urls import url   # 函数url用于创建URL映射到视图

from . import views    # 当前的urls.py模块所在的文件夹中导入视图

app_name=''learning_logs''  # 防止出现错误:Specifying a namespace in include() without providing an app_name

urlpatterns = [
    # 主页
    url(r''^$'', views.index, name=''index''),  
]

url()函数接受三个实参:

  • 第一个是一个正则表达式,查找找开头和末尾之间没有任何东西的URL;
  • 第二个指定了要调用的视图函数(下节编写);
  • 第三个实参将 这个URL模式的名称指定为index,让我们能够在代码的其他地方引用它。每当需要提供到这个主页的链接时,我们都将使用这个名称,而不编写URL。

(2)编写视图

修改learning_logs中的文件views.py文件:

from django.shortcuts import render

def index(request):
    """学习笔记的主页"""
    return render(request, ''learning_logs/index.html'')

URL请求与我们刚才定义的模式匹配时,Django将在文件views.py中查找函数index() ,再将请求对象传递给这个视图函数。在这里,我们不需要处理任何数据,因此这个函数只 包含调用render() 的代码。这里向函数render() 提供了两个实参:原始请求对象以及一个可用于创建网页的模板。下面来编写这个模板。

(3)编写模板

模板定义了网页的结构。模板指定了网页是什么样的,而每当网页被请求时,Django将填入相关的数据。模板让你能够访问视图提供的任何数据。我们的主页视图没有提供任何数据,因此相应的模板非常简单。

在文件夹learning_logs中新建一个文件夹,并将其命名为templates。在文件夹templates中,再新建一个文件夹,并将其命名为learning_logs。这好像有点多余(我们在文件夹 learning_logs中创建了文件夹templates,又在这个文件夹中创建了文件夹learning_logs),但建立了Django能够明确解读的结构,即便项目很大,包含很多应用程序亦如此。在最里面 的文件夹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>

现在,如果你请求这个项目的基础URL——http://localhost:8000/,将看到刚才创建的网页,而不是默认的Django网页。Django接受请求的URL,发现该URL与模式r''^$'' 匹配,因此 调用函数views.index() ,这将使用index.html包含的模板来渲染网页,结果如下图所示。

 15、创建其他网页

 我们将创建两个显示数据的网页,其中一个列出所有的主题,另一个显示特定主题的所有条目。对于每个网页,我们 都将指定URL模式,编写一个视图函数,并编写一个模板。但这样做之前,我们先创建一个父模板,项目中的其他模板都将继承它。

15.1 模板继承

创建网站时,几乎都有一些所有网页都将包含的元素。在这种情况下,可编写一个包含通用元素的父模板,并让每个网页都继承这个模板,而不必在每个网页中重复定义这些通
用元素。这种方法能让你专注于开发每个网页的独特方面,还能让修改项目的整体外观容易得多。

(1)父模板

我们首先来创建一个名为base.html的模板,并将其存储在index.html所在的目录中。这个文件包含所有页面都有的元素;其他的模板都继承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模式匹配。在这个示例中,learning_logs 是一个命名空间 ,而index 是该命名空间中一个名称独特的URL模式。

我们插入了一对块标签。这个块名为content ,是一个占位符,其中包含的信息将由子模板指定。子模板并非必须定义父模板中的每个块,因此在父模板中,可使用任意多个块来预留空间,而子模板可根据需要定义相应数量的块。

在Python代码中,我们几乎总是缩进四个空格。相比于Python文件,模板文件的缩进层级更多,因此每个层级通常只缩进两个空格。

(2)子模块

现在需要重新编写index.html,使其继承base.html,如下所示:

{% extends "learning_logs/base.html" %}

{% block content %}
<p>Learning Log</p>
<p>Learning Log helps you keep track of your learning, for any topic you''re learning about.</p>
{% endblock content %}

如果将这些代码与原来的index.html进行比较,可发现我们将标题LearningLog替换成了从父模板那里继承的代码。子模板的第一行必须包含标签{% extends %},让 Django知道它继承了哪个父模板。文件base.html位于文件夹learning_logs中,因此父模板路径中包含learning_logs。这行代码导入模板base.html的所有内容,让index.html能够指定要 在content 块预留的空间中添加的内容。
我们插入了一个名为content的{% block %}标签,以定义content块。不是从父模板继承的内容都包含在content块中,在这里是一个描述项目“学习笔记”的 段落。我们使用标签{% endblock content %}指出了内容定义的结束位置。
模板继承的优点开始显现出来了:在子模板中,只需包含当前网页特有的内容。这不仅简化了每个模板,还使得网站修改起来容易得多。要修改很多网页都包含的元素,只需在父模板中修改该元素,你所做的修改将传导到继承该父模板的每个页面。在包含数十乃至数百个网页的项目中,这种结构使得网站改进起来容易而且快捷得多。
在大型项目中,通常有一个用于整个网站的父模板——base.html,且网站的每个主要部分都有一个父模板。每个部分的父模板都继承base.html,而网站的每个网 页都继承相应部分的父模板。这让你能够轻松地修改整个网站的外观、网站任何一部分的外观以及任何一个网页的外观。这种配置提供了一种效率极高的工作方式, 让你乐意不断地去改进网站。

15.2 显示所有主题的页面

(1)URL模式:首先,我们来定义显示所有主题的页面的URL。通常,使用一个简单的URL片段来指出网页显示的信息;我们将使用单词topics,因此URL http://localhost:8000/topics/将返回显示所有 主题的页面。下面演示了该如何修改learning_logs/urls.py:

"""定义learning_logs的URL模式"""

from django.conf.urls import url

from . import views
app_name=''learning_logs''

urlpatterns = [
    # 主页
    url(r''^$'', views.index, name=''index''),
    
    # 显示所有的主题
    url(r''^topics/$'', views.topics, name=''topics''),
]

我们只是在用于主页URL的正则表达式中添加了topics/。Django检查请求的URL时,这个模式与这样的URL匹配:基础URL后面跟着topics 。可以在末尾包含斜 杠,也可以省略它,但单词topics 后面不能有任何东西,否则就与该模式不匹配。其URL与该模式匹配的请求都将交给views.py中的函数topics() 进行处理。

(2)视图:函数topics() 需要从数据库中获取一些数据,并将其发送给模板。我们需要在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() 。

(3)模板:显示所有主题的页面的模板接受字典context ,以便能够使用topics() 提供的数据。请创建一个文件,将其命名为topics.html,并存储到index.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 %}

就像模板index.html一样,我们首先使用标签{% extends %}来继承base.html,再开始定义content块。这个网页的主体是一个项目列表,其中列出了用户输入的主题。在标准 HTML中,项目列表被称为无序列表,用标签<ul></ul> 表示。
我们使用了一个相当于for 循环的模板标签,它遍历字典context 中的列表topics 。模板中使用的代码与Python代码存在一些重要差别:Python使用缩进来指出哪些 代码行是for循环的组成部分,而在模板中,每个for循环都必须使用{% endfor %}标签来显式地指出其结束位置。因此在模板中,循环类似于下面这样:

{% for item in list %}
  do something with each item
{% endfor %}

在循环中,我们要将每个主题转换为一个项目列表项。要在模板中打印变量,需要将变量名用双花括号括起来。每次循环时,代码{{ topic }}都被替换为topic的当 前值。这些花括号不会出现在网页中,它们只是用于告诉Django我们使用了一个模板变量。HTML标签<li></li> 表示一个项目列表项,在标签对<ul></ul> 内部,位于标 签<li> 和</li> 之间的内容都是一个项目列表项。
我们使用了模板标签{% empty %},它告诉Django在列表topics为空时该怎么办:这里是打印一条消息,告诉用户还没有添加任何主题。最后两行分别结束for循 环和项目列表。
现在需要修改父模板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 。这一行让Django生成一个链接,它 与learning_logs/ urls.py中名为topics 的URL模式匹配。
现在如果你刷新浏览器中的主页,将看到链接Topics。单击这个链接,将看到类似于图18-4所示的网页。

 15.3 显示特定主题的页面

1、URL模式

显示特定主题的页面的URL模式与前面的所有URL模式都稍有不同,因为它将使用主题的id 属性来指出请求的是哪个主题。例如,如果用户要查看主题Chess(其id 为1)的详细页面,URL将为http://localhost:8000/topics/1/。下面是与这个URL匹配的模式,它包含在learning_logs/urls.py中:

"""定义learning_logs的URL模式"""

from django.conf.urls import url

from . import views
app_name=''learning_logs''

urlpatterns = [
    # 主页
    url(r''^$'', views.index, name=''index''),
    
    # 显示所有的主题
    url(r''^topics/$'', views.topics, name=''topics''),

    # 制定主题的详细页面
    url(r''^topics/(?P<topic_id>\d+)/$'', views.topic, name=''topic'')
]

r''^topics/(?P<topic_id>\d+)/$'' 。

r 让Django将这个字符串视为原始字符串,并指出正则表达式包含在引号内。

(/(?P<topic_id>\d+)/ )与包含在两个斜杠内的整数匹配,并将这个整数存储在一个名为topic_id 的实参中。这部分表达式两边的括号捕获URL中的 值;?P<topic_id> 将匹配的值存储到topic_id 中;而表达式\d+ 与包含在两个斜杆内的任何数字都匹配,不管这个数字为多少位。
发现URL与这个模式匹配时,Django将调用视图函数topic() ,并将存储在topic_id 中的值作为实参传递给它。在这个函数中,我们将使用topic_id 的值来获取相应的主 题。

2、视图

函数topic() 需要从数据库中获取指定的主题以及与之相关联的所有条目,如下所示:

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() 来获取指定的主题,就像前面在Django shell中所做的那样。

我们获取与该主题相关联的条目,并将它们按date_added 排序:date_added 前面的减号 指定按降序排列,即先显示最近的条目。我们将主题和条目都存储在字典context 中,再将这个字典发送给模板topic.html。
在自己的项目中编写这样的查询时,先在Django shell中进行尝试大有裨益。相比于编写视 图和模板,再在浏览器中检查结果,在shell中执行代码可更快地获得反馈。

(3)模板

这个模板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 %}

像这个项目的其他页面一样,这里也继承了base.html。接下来,我们显示当前的主题,它存储在模板变量{{ topic }}中。为什么可以使用变量topic呢?因为它包含在字典context 中。接下来,我们开始定义一个显示每个条目的项目列表,并像前面显示所有主题一样遍历条目。
每个项目列表项都将列出两项信息:条目的时间戳和完整的文本。为列出时间戳,我们显示属性date_added 的值。在Django模板中,竖线(| )表示模板过滤器—— 对模板变量的值进行修改的函数。过滤器date: ''M d, Y H:i''以这样的格式显示时间戳:January1,2015 23:00。接下来的一行显示text的完整值,而不仅仅是entry的前 50个字符。过滤器linebreaks将包含换行符的长条目转换为浏览器能够理解的格式,以免显示为一个不间断的文本块。我们使用模板标签{% empty %}打 印一条消息,告诉用户当前主题还没有条目。

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

 在浏览器中查看显示特定主题的页面前,我们需要修改模板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 。现在,主题列表中的每个主题都是一个链接,链接到显示相应主题的页面,如http://localhost:8000/topics/1/。
如果你刷新显示所有主题的页面,再单击其中的一个主题,将看到类似于图18-5所示的页面。

 

参考资料:

1、python编程,从入门到实践

Django web编程3 -- 创建用户账户

Django web编程3 -- 创建用户账户

我们将建立一个用户注册和身份验证系统,让用户能够注册账户,进而登录和注销。我们将创建一个新的应用程序,其中包含与处理用户账户相关的所有功能。我们还将对模型Topic 稍做修改,让每个主题都归属于特定用户。

1、应用程序users

我们首先使用命令startapp 来创建一个名为users 的应用程序:

(ll_env)learning_log$ python manage.py startapp users
(ll_env)learning_log$ ls
db.sqlite3 learning_log learning_logs ll_env manage.py users
(ll_env)learning_log$ ls users
admin.py __init__.py migrations models.py tests.py views.py

这个命令新建一个名为users的目录,其结构与应用程序learning_logs 相同。

1.1 将应用程序users 添加到settings.py中

在settings.py中,我们需要将这个新的应用程序添加到INSTALLED_APPS 中,如下所示:

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

    # 我的应用程序
    ''learning_logs'',
    ''users'',
]

1.2 包含应用程序users 的URL

接下来,我们需要修改项目根目录中的urls.py,使其包含我们将为应用程序users 定义的URL:

from django.contrib import admin
from django.conf.urls import url, include

urlpatterns = [
    url(r''^admin/'', admin.site.urls),
    url(r'''', include(''learning_logs.urls'',namespace=''learning_logs'')),
    url(r''^users/'', include(''users.urls'', namespace=''users'')),
]

我们添加了一行代码,以包含应用程序users 中的文件urls.py。这行代码与任何以单词users打头的URL(如http://localhost:8000/users/login/)都匹配。我们还创建了命名空 间''users'' ,以便将应用程序learning_logs 的URL同应用程序users 的URL区分开来。

2、登陆页面

我们首先来实现登录页面的功能。为此,我们将使用Django提供的默认登录视图,因此URL模式会稍有不同。在目录learning_log/users/中,新建一个名为urls.py的文件,并在其中添加如下代码:

from django.conf.urls import url
from django.contrib.auth.views import LoginView

from . import views
app_name=''users''

urlpatterns = [
    # 登录页面
    url(r''^login/$'', LoginView.as_view(template_name=''users/login.html''),
       name=''login''),

]

我们首先导入了默认视图login 。登录页面的URL模式与URL http://localhost:8000/users/login/匹配。这个URL中的单词users让Django在users/urls.py中查找,而单词 login让它将请求发送给Django默认视图login (请注意,视图实参为login ,而不是views.login )。鉴于我们没有编写自己的视图函数,我们传递了一个字典,告诉Django 去哪里查找我们将编写的模板。这个模板包含在应用程序users 而不是learning_logs 中。

(1)模板login.html

用户请求登录页面时,Django将使用其默认视图login ,但我们依然需要为这个页面提供模板。为此,在目录learning_log/users/中,创建一个名为templates的目录,并在其中创建一个名为users的目录。以下是模板login.html,你应将其存储到目录learning_log/users/templates/users/中:

{% extends "learning_logs/base.html" %}

{% block content %}

{% if form.errors %}
  <p>Your username and password didn''t match. Please try again.</p>
{% endif %}

  <form method="post" action="{% url ''users:login'' %}">
  {% csrf_token %}
  {{ form.as_p }}

  <button name="submit">log in</button>
  <input type="hidden" name="next" value="{% url ''learning_logs:index'' %}" />
  </form>

{% endblock content %}
  • 这个模板继承了base.html,旨在确保登录页面的外观与网站的其他页面相同。请注意,一个应用程序中的模板可继承另一个应用程序中的模板。
  • 如果表单的errors 属性被设置,我们就显示一条错误消息,指出输入的用户名—密码对与数据库中存储的任何用户名—密码对都不匹配。

我们要让登录视图处理表单,因此将实参action 设置为登录页面的URL。登录视图将一个表单发送给模板,在模板中,我们显示这个表单并添加一个提交按 钮。我们包含了一个隐藏的表单元素——''next'' ,其中的实参value 告诉Django在用户成功登录后将其重定向到什么地方——在这里是主页。

(2)链接到登陆页面

下面在learning_logs/templates/learning_logs/base.html中添加到登录页面的链接,让所有页面都包含它。用户已登录时,我们不想显示这个链接,因此将它嵌套在一个{% if %}标签中:

<p>
    <a href="{% url ''learning_logs:index'' %}">Learning Log</a>
    <a href="{% url ''learning_logs:topics'' %}">Topics</a>
    {% if user.is_authenticated %}
      Hello, {{ user.username }}.
    {% else %}
        <a href="{% url ''users:login'' %}">log in</a>
    {% endif %}
</p>

{% block content %}{% endblock content %}

在Django身份验证系统中,每个模板都可使用变量user ,这个变量有一个is_authenticated 属性:如果用户已登录,该属性将为True ,否则为False 。这让你能够向已 通过身份验证的用户显示一条消息,而向未通过身份验证的用户显示另一条消息。
在这里,我们向已登录的用户显示一条问候语。对于已通过身份验证的用户,还设置了属性username ,我们使用这个属性来个性化问候语,让用户知道他已登录。对于还未通过身份验证的用户,我们再显示一个到登录页面的链接。
(3)使用登陆页面

前面建立了一个用户账户,下面来登录一下,看看登录页面是否管用。请访问http://localhost:8000/admin/,如果你依然是以管理员的身份登录的,请在页眉上找到注销链接并单击
它。
注销后,访问http://localhost:8000/users/login/,你将看到类似于图19-4所示的登录页面。输入你在前面设置的用户名和密码,将进入页面index。。在这个主页的页眉中,显示了一条 个性化问候语,其中包含你的用户名。

 3、注销

 现在需要提供一个让用户注销的途径。我们不创建用于注销的页面,而让用户只需单击一个链接就能注销并返回到主页。为此,我们将为注销链接定义一个URL模式,编写一个 视图函数,并在base.html中添加一个注销链接。

(1)注销url

下面的代码为注销定义了URL模式,该模式与URL http://locallwst:8000/users/logout/匹配。修改后的users/urls.py如下:

from django.conf.urls import url
from django.contrib.auth.views import LoginView

from . import views
app_name=''users''

urlpatterns = [
    # 登录页面
    url(r''^login/$'', LoginView.as_view(template_name=''users/login.html''),
       name=''login''),

    # 注销
    url(r''^logout/$'', views.logout_view, name=''logout''),
]

这个URL模式将请求发送给函数logout_view() 。这样给这个函数命名,旨在将其与我们将在其中调用的函数logout() 区分开来。

2. 视图函数logout_view()

函数logout_view() 很简单:只是导入Django函数logout() ,并调用它,再重定向到主页。请打开users/views.py,并输入下面的代码:

from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth import logout

def logout_view(request):
    """注销用户"""
    logout(request)
    return HttpResponseRedirect(reverse(''learning_logs:index''))

我们从django.contrib.auth中导入了函数logout()。我们调用了函数logout() ,它要求将request 对象作为实参。然后,我们重定向到主页。

(3)链接到注销页面

现在我们需要添加一个注销链接。我们在learning_logs/templates/learning_logs/base.html 中添加这种链接,让每个页面都包含它;我们将它放在标签{% if user.is_authenticated %}中,使得仅当用户登录后 才能看到它:

<p>
    <a href="{% url ''learning_logs:index'' %}">Learning Log</a>
    <a href="{% url ''learning_logs:topics'' %}">Topics</a>
    {% if user.is_authenticated %}
      Hello, {{ user.username }}.
      <a href="{% url ''users:logout'' %}">log out</a>
    {% else %}
        <a href="{% url ''users:login'' %}">log in</a>
    {% endif %}
</p>

{% block content %}{% endblock content %}

下图显示了用户登录后看到的主页。这里的重点是创建能够正确工作的网站,因此几乎没有设置任何样式。确定所需的功能都能正确运行后,我们将设置这个网站的样式,使 其看起来更专业。

4、注册页面

下面来创建一个让新用户能够注册的页面。我们将使用Django提供的表单UserCreationForm,但编写自己的视图函数和模板。

(1)注册页面的URL模式

下面的代码定义了注册页面的URL模式,它也包含在users/urls.py中:

from django.conf.urls import url
from django.contrib.auth.views import LoginView

from . import views
app_name=''users''

urlpatterns = [
    # 登录页面
    url(r''^login/$'', LoginView.as_view(template_name=''users/login.html''),
       name=''login''),

    # 注销
    url(r''^logout/$'', views.logout_view, name=''logout''),

    # 注册页面
    url(r''^register/$'', views.register, name=''register''),
]

(2). 视图函数register()

在注册页面首次被请求时,视图函数register() 需要显示一个空的注册表单,并在用户提交填写好的注册表单时对其进行处理。如果注册成功,这个函数还需让用户自动登 录。请在users/views.py中添加如下代码:

from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.forms import UserCreationForm

def logout_view(request):
    """注销用户"""
    logout(request)
    return HttpResponseRedirect(reverse(''learning_logs:index''))

def register(request):
    """注册新用户"""
    if request.method != ''POST'':
        # 显示空的注册表单
        form = UserCreationForm()
    else:
        # 处理填写好的表单
        form = UserCreationForm(data=request.POST)

        if form.is_valid():
            new_user = form.save()
            # 让用户自动登录,再重定向到主页
            authenticated_user = authenticate(username=new_user.username,
                password=request.POST[''password1''])
            login(request, authenticated_user)
            return HttpResponseRedirect(reverse(''learning_logs:index''))

    context = {''form'': form}
    return render(request, ''users/register.html'', context)
  • 我们首先导入了函数render() ,然后导入了函数login() 和authenticate() ,以便在用户正确地填写了注册信息时让其自动登录。我们还导入了默认表 单UserCreationForm 。
  • 在函数register() 中,我们检查要响应的是否是POST请求。如果不是,就创建一个UserCreationForm 实例,且不给它提供任何初始数据。如果响应的是POST请求,我们就根据提交的数据创建一个UserCreationForm 实例,并检查这些数据是否有效:就这里而言,是用户名未包含非法字符,输入的两个密码相同,以及用户没有试图做恶意的事情。
  • 如果提交的数据有效,我们就调用表单的方法save() ,将用户名和密码的散列值保存到数据库中。方法save() 返回新创建的用户对象,我们将其存储在new_user 中。
  • 保存用户的信息后,我们让用户自动登录,这包含两个步骤。首先,我们调用authenticate() ,并将实参new_user.username 和密码传递给它。用户注册时, 被要求输入密码两次;由于表单是有效的,我们知道输入的这两个密码是相同的,因此可以使用其中任何一个。在这里,我们从表单的POST数据中获取与键''password1'' 相关 联的值。如果用户名和密码无误,方法authenticate() 将返回一个通过了身份验证的用户对象,而我们将其存储在authenticated_user 中。
  • 我们调用函 数login() ,并将对象request 和authenticated_user 传递给它,这将为新用户创建有效的会话。最后,我们将用户重定向到主页,其页眉中显示了 一条个性化的问候语,让用户知道注册成功了。

 (3)注册模板

注册页面的模板与登录页面的模板类似,请务必将其保存到login.html所在的目录中:

{% extends "learning_logs/base.html" %}

{% block content %}

  <form method="post" action="{% url ''users:register'' %}">
  {% csrf_token %}
  {{ form.as_p }}

  <button name="submit">register</button>
  <input type="hidden" name="next" value="{% url ''learning_logs:index'' %}" />
</form>

{% endblock content %}

这里也使用了方法as_p ,让Django在表单中正确地显示所有的字段,包括错误消息——如果用户没有正确地填写表单。

(4)链接到注册页面

我们在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>
    {% if user.is_authenticated %}
      Hello, {{ user.username }}.
      <a href="{% url ''users:logout'' %}">log out</a>
    {% else %}
        <a href="{% url ''users:register'' %}">register</a>
        <a href="{% url ''users:login'' %}">log in</a>
    {% endif %}
</p>

{% block content %}{% endblock content %}

现在,已登录的用户看到的是个性化的问候语和注销链接,而未登录的用户看到的是注册链接和登录链接。请尝试使用注册页面创建几个用户名各不相同的用户账户。
注意  这里的注册系统允许用户创建任意数量的账户。有些系统要求用户确认其身份:发送一封确认邮件,用户回复后其账户才生效。通过这样做,系统生成的垃圾账户将比这里使用的简单系统少。然而,学习创建应用程序时,完全可以像这里所做的那样,使用简单的用户注册系统。

 5、让用户拥有自己的数据

 用户应该能够输入其专有的数据,因此我们将创建一个系统,确定各项数据所属的用户,再限制对页面的访问,让用户只能使用自己的数据。
在本节中,我们将修改模型Topic ,让每个主题都归属于特定用户。这也将影响条目,因为每个条目都属于特定的主题。我们先来限制对一些页面的访问。

 5.1、使用@login_required 限制访问

Django提供了装饰器@login_required ,让你能够轻松地实现这样的目标:对于某些页面,只允许已登录的用户访问它们。装饰器 (decorator)是放在函数定义前面的指 令,Python在函数运行前,根据它来修改函数代码的行为。下面来看一个示例。
1. 限制对topics 页面的访问
每个主题都归特定用户所有,因此应只允许已登录的用户请求topics 页面。为此,在learning_logs/views.py中添加如下代码: views.py

 

from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth.decorators import login_required

from .models import Topic, Entry
from .forms import TopicForm,EntryForm

def index(request):
    """学习笔记的主页"""
    return render(request, ''learning_logs/index.html'')

@login_required
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)

def new_topic(request):
    """添加新主题"""
    if request.method != ''POST'':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse(''learning_logs:topics''))
    context = {''form'': form}
    return render(request, ''learning_logs/new_topic.html'', context)

def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != ''POST'':
        # 未提交数据,创建一个空表单
        form = EntryForm()
    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=request.POST)
        if form.is_valid():
            new_entry = form.save(commit=False)
            new_entry.topic = topic
            new_entry.save()
            return HttpResponseRedirect(reverse(''learning_logs:topic'',
                                                args=[topic_id]))
    context = {''topic'': topic, ''form'': form}
    return render(request, ''learning_logs/new_entry.html'', context)

def edit_entry(request, entry_id):
    """编辑既有条目"""
    entry = Entry.objects.get(id=entry_id)
    topic = entry.topic

    if request.method != ''POST'':
        # 初次请求,使用当前条目填充表单
        form = EntryForm(instance=entry)
    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(instance=entry, data=request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse(''learning_logs:topic'',
                                                args=[topic.id]))
    context = {''entry'': entry, ''topic'': topic, ''form'': form}
    return render(request, ''learning_logs/edit_entry.html'', context)

我们首先导入了函数login_required() 。我们将login_required() 作为装饰器用于视图函数topics() ——在它前面加上符号@ 和login_required ,让Python在运 行topics() 的代码前先运行login_required() 的代码。
login_required() 的代码检查用户是否已登录,仅当用户已登录时,Django才运行topics() 的代码。如果用户未登录,就重定向到登录页面。 为实现这种重定向,我们需要修改settings.py,让Django知道到哪里去查找登录页面。请在项目learning_log的Django设置settings.py末尾添加如下代码:

# 我的设置
LOGIN_URL = ''/users/login/''

现在,如果未登录的用户请求装饰器@login_required 的保护页面,Django将重定向到settings.py中的LOGIN_URL 指定的URL。
要测试这个设置,可注销并进入主页。然后,单击链接Topics,这将重定向到登录页面。接下来,使用你的账户登录,并再次单击主页中的Topics链接,你将看到topics页面。

2、全面限制对项目“学习笔记”的访问

Django让你能够轻松地限制对页面的访问,但你必须针对要保护哪些页面做出决定。最好先确定项目的哪些页面不需要保护,再限制对其他所有页面的访问。你可以轻松地修改 过于严格的访问限制,其风险比不限制对敏感页面的访问更低。
在项目“学习笔记”中,我们将不限制对主页、注册页面和注销页面的访问,并限制对其他所有页面的访问。 在下面的learning_logs/views.py中,对除index() 外的每个视图都应用了装饰器@login_required :

--snip-- 
@login_required
def topics(request):
    --snip--

@login_required
def topic(request, topic_id):
    --snip--

@login_required
def new_topic(request):
    --snip--

@login_required
def new_entry(request, topic_id):
    --snip--

@login_required
def edit_entry(request, entry_id):
    --snip--

如果你在未登录的情况下尝试访问这些页面,将被重定向到登录页面。另外,你还不能单击到new_topic 等页面的链接。但如果你输入URL http://localhost:8000/new_topic/,将重 定向到登录页面。对于所有与私有用户数据相关的URL,都应限制对它们的访问。

3、将数据关联到用户

现在,需要将数据关联到提交它们的用户。我们只需将最高层的数据关联到用户,这样更低层的数据将自动关联到用户。例如,在项目“学习笔记”中,应用程序的最高层数据是
主题,而所有条目都与特定主题相关联。只要每个主题都归属于特定用户,我们就能确定数据库中每个条目的所有者。
下面来修改模型Topic ,在其中添加一个关联到用户的外键。这样做后,我们必须对数据库进行迁移。最后,我们必须对有些视图进行修改,使其只显示与当前登录的用户相关 联的数据。

(1)修改模型Topic

对models.py的修改只涉及两行代码:

from django.db import models
from django.contrib.auth.models import User

class Topic(models.Model): 
    """用户要学习的主题"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True) 
    owner = models.ForeignKey(User)

    def __str__(self): 
        """返回模型的字符串表示""" 
        return self.text

class Entry(models.Model): --snip--

我们首先导入了django.contrib.auth 中的模型User ,然后在Topic 中添加了字段owner ,它建立到模型User 的外键关系。

(2) 确定当前有哪些用户 

 我们迁移数据库时,Django将对数据库进行修改,使其能够存储主题和用户之间的关联。为执行迁移,Django需要知道该将各个既有主题关联到哪个用户。最简单的办法是,将既 有主题都关联到同一个用户,如超级用户。为此,我们需要知道该用户的ID。
下面来查看已创建的所有用户的ID。为此,启动一个Django shell会话,并执行如下命令:

(ll_env) [root@xxjt learning_log]# python3 manage.py shell>>> from django.contrib.auth.models import User
>>> User.objects.all()
<QuerySet [<User: happy>, <User: test1234>]>
>>> 
>>> for user in User.objects.all():
...     print(user.username, user.id)
... 
happy 1
test1234 2

我们在shell会话中导入了模型User 。然后,我们查看到目前为止都创建了哪些用户。 我们遍历用户列表,并打印每位用户的用户名和ID。Django询问要将既有主题关联到哪个用户时,我们将指定其中的一个ID值。

3. 迁移数据库

知道用户ID后,就可以迁移数据库了。

 

 

参考资料:

1、python编程,从入门到实践

今天关于golang web编程之——读取csdn最新文章golang读取文件的讲解已经结束,谢谢您的阅读,如果想了解更多关于Cocos2d-x——读取Csv配置文件、CSDN文章列表的CSS实现_html/css_WEB-ITnose、Django web编程1 -- 创建项目和应用、Django web编程3 -- 创建用户账户的相关知识,请在本站搜索。

本文标签: