============================
Django Step by Step (五)
============================

:作者: limodou
:联系: limodou@gmail.com
:版本: 0.1
:主页: http://wiki.woodpecker.org.cn/moin/NewEdit
:BLOG: http://www.donews.net/limodou
:版权: FDL

.. contents:: 目录
.. sectnum::

引言
=====

其实本教程以展示基本概念为已任，对于一些高级的话题我也在不停地学习中，希望能有所展示。让我们一起学习吧。

在了解了基本的 Django 开发的过程及 Django 的一些基本特性之后，越来越多的东西在等着我们。现在我们就学习一下 session 吧。 session 可以翻译为“会话”，做过web的可能都知道。它就是为了实现页面间的数据交换而产生的东西，一般有一个 session_id ，它会保存在浏览器的 cookie 中，因此如果你的浏览器禁止了 cookie ，下面的试验是做不了的。

在 Django 中的 session 也非常简单，它就存在于 ``request`` 对象的 ``session`` 属性中。你可以把它看成一个字典就可以了。

下面我们做一个非常简单的功能：首先当用户进入某个页面，这个页面会显示一个登录页面，上面有一个文本框用来输入用户名，还有一个提交按钮用来提交数据。当用户输入用户名，然后点提交，则显示显示用户已经登录，并且打印出用户的姓名来，同时还提供一个“注销”按钮。然后如果用户再次进入这个页面，则显示同登录成功后的页面。如果点击注销则重新进入未登录的页面。

在newtest下创建 login.py
==========================

::

    from django.http import HttpResponseRedirect
    from django.shortcuts import render_to_response
    
    def login(request):
        username = request.POST.get('username', None)
        if username:
            request.session['username'] = username
        username = request.session.get('username', None)
        if username:
            return render_to_response('login.html', {'username':username})
        else:
            return render_to_response('login.html')
        
    def logout(request):
        try:
            del request.session['username']
        except KeyError:
            pass
        return HttpResponseRedirect("/login/")

有些复杂了吗？没关系，让我解释一下。这里有两个方法： ``login()`` 和 ``logout()`` 。 ``login()`` 用来提供初始页面、处理提供数据和判断用户是否登录。而 ``logout()`` 只是用来从 session 中删除用户名，同时将页面重定向到 login 画面。这里我仍然使用了模板，并且根据传入不同的字典来控制模板的生成。是的，因为 Django 的模块支持条件判断，所以可以做到。

在 ``login()`` 中的判断逻辑是：

    - 先从 POST 中取 ``username`` (这样 ``username`` 需要由模板的 form 来提供)，如果存在则加入到 session 中去。加入 session 很简单，就是一个字典的 Key 赋值。

    - 然后再从 session 中取 ``username`` ，有两种可能：一种是上一步实现的。还有一种可能是直接从以前的 session 中取出来的，它不是新产生的。而这里并没有细分这两种情况。因此这个判断其实对应两种页面请求的处理：一种是提交了用户姓名，而另一种则是处理完用户提交姓名之后，用户再次进入的情况。而用户再次进入时，由于我们在前面已经将他的名字保存在 session 里面了，因此可以直接取出来。如果 session 中存在，则表示用户已经登录过，则输出 ``login.html`` 模板，同时传入了 ``username`` 字典值。而如果 session 中不存在，说明用户从来没有登录过，则输出 ``login.html`` 模板，这次不带值。

因此对于同一个 ``login.html`` 模板传入的不同值，后面我们会看到模板是如何区分的。

在 ``logout()`` 中很简单。先试着删除 session ，然后重定向页面到 login 页面。这里使用了 ``HttpResponseRedirect`` 方法，它是从以前我们看到的 ``HttpResponse`` 派生来的子类。更多的派生子类和关于 response 的内容要参考 `Request and response objects`_ 文档。

.. _`Request and response objects`: http://www.djangoproject.com/documentation/request_response/

创建 templates/login.html
============================

::

    {% if not username %}
    <form method="post" action="/login/">
        用户名：<input type="text" name="username" value=""><br/>
        <input type="submit" value="登录">
    </form>
    {% else %}
    你已经登录了！{{ username }}<br/>
    <form method="post" action="/logout/">
        <input type="submit" value="注销">
    </form>
    {% endif %}

整个是一个 ``if`` 语句。在 Django 模板中的 ``if`` 可以象 Python_ 一样使用，如使用 ``not`` , ``and`` , ``or`` 。象 ``if not username`` 表示什么呢？它表示如果 ``username`` 不存在，或为空，或是假值等等。而此时我们利用了 ``username`` 不存在这种判断。

.. _Python: http://www.python.org/

上面的逻辑表示，如果 ``username`` 不存在，则显示一个表单，显示用户名输入文本框。如果存在，则显示已经登录信息，同时显示用户名和注销按钮。而这个注销铵钮对应于 ``logout()`` 方法。

修改 urls.py
===============

::

    from django.conf.urls.defaults import *
    
    urlpatterns = patterns('',
        # Example:
        # (r'^testit/', include('newtest.apps.foo.urls.foo')),
        (r'^$', 'newtest.helloworld.index'),
        (r'^add/$', 'newtest.add.index'),
        (r'^list/$', 'newtest.list.index'),
        (r'^csv/(?P<filename>\w+)/$', 'newtest.csv_test.output'),
        (r'^login/$', 'newtest.login.login'),
        (r'^logout/$', 'newtest.login.logout'),
    
        # Uncomment this for admin:
    #     (r'^admin/', include('django.contrib.admin.urls')),
    )

增加了 login 和 logout 两个url映射。

启动 server 运行
==================

但我要说，你一定会报错。而且我的也在报错。为什么，因为从这一刻起，我们就要进入有数据库的环境了。因为在 django 中 session 是存放在数据库中的。所以在这里要进行数据库的初始化了。

修改 settings.py
==================

主要修改以下地方::

    DATABASE_ENGINE = 'sqlite3'
    DATABASE_NAME = './data.db'
    DATABASE_USER = ''
    DATABASE_PASSWORD = ''
    DATABASE_HOST = ''
    DATABASE_PORT = ''

这里我使用 sqlite3 。在使用数据库时，你同时需要自已去安装相应的数据库处理模块。对于 sqlite3 只需要修改两项： ``DATABASE_ENGINE`` 和 ``DATABASE_NAME`` 。这里数据文件名我使用了相对路径，我想在实际情况下可能使用绝对路径为好。

.. note:: sqlite3 对应的是 pysqlite2 模块，可以从 http://pysqlite.org/ 下载到最新版本。

.. note:: 如果你使用utf-8，要注意缺省字符编码应为utf-8。同时对于 ``DATABASE_PORT`` 字段，Django 目前允许使用数字或字符串表示了。

初始化数据库
==============

改了配置还不够，还要执行相应的建库、建表的操作，使用 ``django-admin.py`` 或 ``manage.py`` 都可以::

    manage.py syncdb
    
.. note:: 原来使用 init 命令。但后来Magic-removal分枝删除了 init 命令，新增了 syncdb 命令。它可以自动创建已经安装的 App 中，在数据库中不存在的 Model。并且会自动将表的权限赋与超级用户。并且把超级用户的创建也结合到了一起。

启动 server
============

这次再进入试吧

http://localhost:8000/login/

从此我们要进入数据库的世界了，当然目前还没有用到，而 Django 提供的许多自动化的高级功能都是需要数据库支持的。