LittleQ

爱好:写代码

pyCharm在调试程序的时候出现了这个问题,解决办法就是在Run--> View Breakpoints里面(或者直接按Ctrl+Shift+F8)
这里可以看到所有的断点,哪怕是其他工程项目里面的,这就会导致几个问题,当你删掉了某些文件的时候,断点记录还在,所以在运行的时候会报错:

pydev debugger: warning: trying to add breakpoint to file that does not exist: /home/workspace/work/Python/upload_file/upload_file/views.py (will have no effect)

原因就是我把这个删掉了,但是之前在调试这个文件的时候设置过断点,所以记录还在,把不需要的断点记录勾选掉就可以了。

Hive参数设置方式

Hive提供了很多可设置参数,可以通过设置不同的参数来满足不同场景的各种需求,改变Hive有三种方式:

  1. 修改Hive安装路径下的配置文件,即修改${HIVE_HOME}/conf/hive-site.xml文件里的配置;
  2. 在启动Hive时,在命令行里面设置参数;
  3. 在进入Hive的CLI客户端里面进行参数设置。

修改配置文件设置参数

在安装好Hive之后,默认的配置会在${hive_home}/conf/hive-default.xml文件里面,一般都会对默认的配置做一定的修改,如果要修改默认配置,可以先在相同目录下创建一个hive-site.xml,格式如下:

1
2
3
4
5
6
7
8
9
<configuration>
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
<description>
location of default database for the warehouse
</description>
</property>
</configuration>

所有的配置都是以<configuration></configuration>标签里面,里面可以有多个<property></property>标签,每个<property></property>标签里面可以设定我们需要设定的属性值:

  1. <name></name>:要设定的属性的属性名
  2. <value></value>:设定属性的值
  3. <description></description>:描述属性的一些介绍性语句,可以不写
    大部分共同的配置都是写到这里的,如果有必要的话,因为这里面的配置对全局用户都有效,并且一旦设置就永久有效。并且hive-site.xml里的配置会覆盖hive-default.xml里的配置,并且由于Hive是作为Hadoop的客户端启动的,所以Hive同时也会读取Hadoop的配置,同理,Hive的配置会覆盖Hadoop的配置。

命令行设置参数

在命令行启动Hive进入CLI的时候,可以在命令行里面添加--hiveconf param=value来设定参数,例如,在终端里输入:

1
$ hive --hiveconf mapreduce.job.queuename=queue1

这个是设置MapReduce任务的队列,注意这个设置是临时的,一旦你退出了Hive客户端,这个配置就失效了,下次如果想使用这个配置,你必须重新配置。

进入Hive的CLI客户端设置参数

当我们已经进入了CLI里面,可以使用set关键字来设置参数,例如:

1
>hive set mapreduce.job.queuename=queue1;

这个和上一种方式差不多,效果也是一样,一旦退出CLI客户端,也会失效,但是和方法二有一个不同的地方是,如果set后面只跟参数名而不带参数值,就可以查看这个参数目前的值,像下面这样:

1
2
>hive set mapreduce.job.queuename;
mapreduce.job.queuename=queue1

如果set后面连参数名都不跟,那么就可以查看整个Hive的所有配置

1
2
3
4
5
6
7
8
9
10
>hive set;
datanucleus.autoCreateSchema=true
datanucleus.autoStartMechanismMode=checked
datanucleus.cache.level2=false
datanucleus.cache.level2.type=none
datanucleus.connectionPoolingType=DBCP
datanucleus.identifierFactory=datanucleus
datanucleus.plugin.pluginRegistryBundleCheck=LOG
datanucleus.storeManagerType=rdbms
datanucleus.transactionIsolation=read-committed

上面介绍的这三种参数设置方式的优先级类似于编程语言里的变量声明,即本地变量会覆盖全局变量,优先级是依此递增的

常用Hive配置

注意,这些设置里面没有双引号

1
2
3
4
5
6
7
8
# 设置job名
set mapred.job.name=这是一个测试;
# 设置job执行队列名
set mapred.job.queue.name=root.tcop;
# 设定reduce内存大小,单位M
set mapreduce.reduce.memory.mb=8048;
set mapred.child.reduce.java.opts=-Xmx8048M;
set mapreduce.reduce.java.opts=-Xmx8048M;

在Ubuntu 14.04 上插硬盘,结果,无法挂载

1
An operation is already pending

解决方案,在终端中运行

1
gsettings set org.gnome.desktop.media-handling automount false

然后把硬盘拔了重新插上就可以挂载了。

官方推荐的安装方法:

  • Debian / Ubuntu : sudo apt-get install python-matplotlib
  • Fedora / Redhat : sudo yum install python-matplotlib

判断是否安装成功的方法,在python交互式环境下:

1
import matplotlib

如果没有报错,则说明安装成功了,如果这样还不成功,可以通过pip来安装,如何安装pip,
参见文章服务器安装pip工具,具体的命令就是:

1
2
3
4
5
6
$ sudo wget --no-check-certificate https://bootstrap.pypa.io/ez_setup.py
$ sudo python ez_setup.py --insecure
$ sudo wget --no-check-certificate https://pypi.python.org/packages/source/p/pip/pip-7.1.2.tar.gz
$ sudo tar -xvf pip-7.1.2.tar.gz
$ cd pip-7.1.2
$ sudo python setup.py install

使用方法

1
$ sudo pip install module_name

如果使用pip安装不成功,可以通过编译matplotlib源码来手动安装,

1
2
3
4
5
$ sudo wget --no-check-certificate https://pypi.python.org/packages/source/m/matplotlib/matplotlib-1.5.1.tar.gz 
$ sudo tar -xvf matplotlib-1.5.1.tar.gz
$ cd matplotlib-1.5.1
$ python setup.py build
$ python setup.py install

但是我在执行python setup.py build的时候,,提示缺少依赖,无法构建,缺少依赖,根据build打印出的信息,把必须的依赖里为no的包都装上,基本就是libpng,freetype这两个,具体如何安装,可以先执行:

1
$ yum list | grep libpng

从打印出来的结果里挑一个适合你系统的版本,记住,如果正统的版本安装了还不行,你还需要安装libpng-devel这种类似的名字的包,是情况而定。
然后所有的依赖安装好了之后,终于build成功了,但是我在执行install的时候,还是出错了,gcc-plus错误,出现这种错误是由于没有装g++的库,安装方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ yum list | grep gcc
gcc.x86_64 4.4.7-3.el6 @base
gcc-c++.x86_64 4.4.7-3.el6 @base
libgcc.x86_64 4.4.7-3.el6 @anaconda-CentOS-201303020151.x86_64/6.4
compat-gcc-34.x86_64 3.4.6-19.el6 base
compat-gcc-34-c++.x86_64 3.4.6-19.el6 base
compat-gcc-34-g77.x86_64 3.4.6-19.el6 base
gcc-gfortran.x86_64 4.4.7-3.el6 base
gcc-gnat.x86_64 4.4.7-3.el6 base
gcc-java.x86_64 4.4.7-3.el6 base
gcc-objc.x86_64 4.4.7-3.el6 base
gcc-objc++.x86_64 4.4.7-3.el6 base
libgcc.i686 4.4.7-3.el6 base
mingw32-gcc.x86_64 4.4.6-4.el6 base
mingw32-gcc-c++.x86_64 4.4.6-4.el6 base
mingw32-gcc-gfortran.x86_64 4.4.6-4.el6 base
mingw32-gcc-objc.x86_64 4.4.6-4.el6 base
mingw32-gcc-objc++.x86_64 4.4.6-4.el6 base

可以看到有很多包,你可以都装上,但是此处我们只需要gcc-c++.x86_64,装完之后,然后重新执行python setup.py install安装即可。然后再进入python交互式环境就可以引用matplotlib包了。

最近写Python脚本,需要在服务器上运行,也没办法用PyCharm这种带界面的IDE来写Python,不过看了一篇博客,讲的十分不错,所以自己照着弄了一遍,中间有些不够详细的我也一并记录下来.

准备环境

OS:Linux/Unix(服务器是CentOS6.4 64bit)
Vim: >=vim 7.3(终端直接输入vim –version 可以查看版本)
Python: 具体的没试过,我的是Python2.7.4

Vim扩展

Vim之所以好用,一方面是自身的快捷键很强大,记熟了确实很好用,但是纵观任何一个流行的编辑器,更为重要的一点是可扩展性极强,每个人都可以定制和扩展,最终打造一个最适合自己的编辑器,这个可以说是每个流行的编辑器,浏览器也是如此得以流行的不可或缺的原因.插件太多,一个一个来装太麻烦,并且多了管理起来也不方便,换个系统又得一个一个安装,太麻烦,所以我们需要的第一个东西是:好用的扩展管理器.
Vim的扩展通常我们叫bundle或者插件

Vundle

Vim的编辑器很多,我就随便选一个了,普通的用也没区别,强烈推荐Vundle.
先来安装Vundle:

1
git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/vundle

上面的这个只是把Vundle插件管理器下载下来了,并且把插件放在了~/.vim/bundle/目录中,现在通过编辑~/.vimrc来配置Vim编辑器来安装Vundle,如果没有则自己创建一个:

1
touch ~/.vimrc

接下在把下面的代码放到~/.vimrc文件顶部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
set nocompatible              " required
filetype off " required

" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/vundle
call vundle#begin()

" alternatively, pass a path where Vundle should install plugins
"call vundle#begin('~/some/path/here')

" let Vundle manage Vundle, required
Plugin 'gmarik/vundle'

" Add all your plugins here (note older versions of Vundle used Bundle instead of Plugin)


" All of your Plugins must be added before the following line
call vundle#end() " required
filetype plugin indent on " required

这样就完成了Vundle的设置,然后就可以安装了,打开Vim编辑器,输入下面的命令:

1
:PluginInstall

这个命令非常好用,它告诉Vundle施展它的魔法——自动下载所有的插件,并为你进行安装和更新。

开始打造IDE

如果你对Vim不熟,可以看看我的这篇文章:Vim-常用命令总结,常用的一些命令基本就在这了.好了,开始正式配置Vim了.

代码折叠

1
2
3
" Enable folding
set foldmethod=indent
set foldlevel=99

我们在实际编程中经常会用到加载路径中的一些指定的文件,这就涉及到路径的拼接,Unix/Linux中的路径是/分隔,但是Windows中路径分隔是\\.所以自己拼接路径容易出错,python直接提供了路径拼接函数帮我们完成这些麻烦的事情.

1
os.path.join(path1,path2,path3....)

它会根据系统的不同,自动选择使用/或者\\来连接两个字符串路径.

但是在服务器上使用这个函数来拼接字符串的时候,如下

1
os.path.join('/home/bin', /python')

输出结果是:

1
'/python'

正常情况下应该输出/home/bin/python,但是好像这个没起作用.
**注意:**这个里面两个路径都是/开头,经测试,这个函数在python2.6.6和python2.7.10+上测试都是一样的,官方给出的解释是:

如果路径前面带有/,那么python会认为这是一个绝对路径,所以就不需要再拼接路径了

**切记:**在使用这个函数拼接绝对路径的时候,最前面那个路径可以带/开头,但是后面的路径开头必须是不带/的一个字符串.

系统版本:CentOS release 6.4 (Final)
Python版本:2.6.6

安装步骤

  1. 先下载安装setuptools

    1
    2
    sudo wget --no-check-certificate https://bootstrap.pypa.io/ez_setup.py
    sudo python ez_setup.py --insecure
  2. 然后下载安装pip

    1
    2
    3
    4
    sudo wget --no-check-certificate https://pypi.python.org/packages/source/p/pip/pip-7.1.2.tar.gz
    sudo tar -xvf pip-7.1.2.tar.gz
    cd pip-7.1.2
    sudo python setup.py install
  3. pip使用方法

    1
    sudo pip install module_name

安装python

有时候用pip安装,会提示:

1
SystemError: Cannot compile 'Python.h'. Perhaps you need to install python-dev|python-devel

这时候不能像Ubuntu上安装python-dev,在CentOS上,名字变了,像如下安装:

1
sudo yum install python-devel

**备注:**如果是Ubuntu安装报错mysql_config:

1
2
3
4
    libs = mysql_config("libs_r")
File "/tmp/pip-build-a7IbwK/MySQL-python/setup_posix.py", line 25, in mysql_config
raise EnvironmentError("%s not found" % (mysql_config.path,))
EnvironmentError: mysql_config not found

解决方案为:

1
sudo apt-get install libmysqlclient-dev

还需要升级python2.6.6至2.7.x
查看python2.7安装位置,假设在/usr/bin/q-python2.7,建立软连接

1
2
# 加-b 参数是为了覆盖之前的软链接
sudo ln -sb /usr/bin/q-python2.7 /usr/bin/python

这会导致yum无法使用,修复办法,编辑/usr/bin/yum
将头部的python改为python2.6

有时候由于国内某些你懂的原因,安装包经常连不上,需要设置pip的国内镜像CentOS修改~/.pip/pip.conf文件,内容如下:

1
2
3
[global]
trusted-host=mirrors.aliyun.com
index-url=http://mirrors.aliyun.com/pypi/simple/

对应的如果是Windows,则需要在对应用户目录下修改,例如C:\Users\zhangsan\pip\pip.ini,内容如上。

指定url安装包可以这样, 以Flask为例:

1
pip install -i http://mirrors.aliyun.com/pypi/simple/ Flask

项目依赖打包以及环境快速恢复:

1
2
3
4
5
# 依赖导出
pip freeze > requirements.txt

# 安装文件中所有依赖
pip install -r requirements.txt

通过第三篇笔记我们也基本知道怎么处理Web的视图和数据库及链接和模板,这次主要讲一讲表单,因为Web免不了要提交数据给服务器,表单作为一种非常常见和基本的提交数据的方式,在Web开发中是很重要的一种方法.

一个简单的表单

更新我们的模板文件,编辑polls/detail.html,改成如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
<h1>{{ question.question_text }}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p><% endif %}

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{forloop.counter}}" value="{{ choice.id}}"/>
<label for="choice{{forloop.counter}}">{{ choice.choice_text }}</label><br/>
<% endfor %}

<input type="submit" value="Vote" />
</form>
  • 上面的是一个简单的单选按钮的模板,每一个投票选项是一个单选按钮,然后这个单选按钮的值就是关联的问题选项的id。每个单选按钮的名字都一样,是choice,也就是如果你选了某个选项,那么POST的数据就是choice=#id

    1
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
  • forloop.counter暗示了有多少个选项,其实就是外层循环次数。

  • {% csrf_token %} 是个防止跨域攻击的标签,只需要知道这么用就行了。

这回我们可以来完善一下我们的vote视图,让它有一些功能,编辑polls/views.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from models import Question, Choice
# 省略其他视图

def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)

try:
select_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# 重新展示问题的投票按钮
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice."
})
else:
select_choice.votes += 1
select_choice.save()

# 投票成功之后要重定向到一个结果网页
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

有一些代码需要解释一下,好多前面并没有介绍过:

  • request.POST就像一个字典一样,你可以通过key来访问表单提交的数据,request.POST['choice']返回的是选项的id,不过返回的是个字符串形式,因为它返回的始终是字符串形式。
  • **request.POST[‘choice’]**会抛出一个KeyError当这个值没有提供的时候,这里我们的处理是没提供就返回到提交投票的表单并且给出一个信息提示。
  • 当投票成功之后,代码中返回了一个HttpResponseRedirect而不是普通的HttpResponse,这个函数还可以带参数,可以重定向到我们想访问的网址,建议当我们成功处理的一个POST请求的时候都可以这么做。
    1
    /polls/3/results/
    这个3就是参数中带的question.id的值。
    当某个人对这个问题投票的时候,vote()视图将会重定向到结果页面,还需要增加一个results()视图,编辑polls/views.py
    1
    2
    3
    def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})
    这个和之前的detail()视图非常相似,用到了results.html模板,新建template/polls/results.html文件:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <h1>{{ question.question_text }}</h1>

    <ul>
    {% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote {{ choice.votes | pluralize }}</li>
    <% endfor %}
    </ul>

    <a href="{% url 'polls:detail' question.id %}">Vote again?</a>
    此时如果你直接运行,访问polls/1/这个链接里面是空的,因为你的数据库里没有Choice,你可以直接在命令行中运行插入,或者一个简单的,直接按照前面讲的,把choice模块注册到admin模块中管理,就可以在网页上直接操作了,加入注册模块也很简单,修改polls/admin.py文件,内容如下:
    1
    2
    3
    4
    5
    6
    7
    from django.contrib import admin

    # Register your models here.
    from models import Question, Choice

    admin.site.register(Question)
    admin.site.register(Choice)
    然后就去管理员后台直接添加几个选项和问题吧。添加完毕然后再访问,就会出现一个问题和对应的投票选项,就像我们做选择题一样,一个题有几个选项。假设我们什么都不选,然后点提交。会得到一个错误信息,就显示在选项上面You didn’t select a choice.

上一篇文章讲了数据库如何存储在数据库中,以及模块如何显示在网页中,利用管理模块添加管理模块。现在我们来做一个简单的投票网站,大概有以下几个页面:

整体框架

  1. indexPage:展示最近最新的问题
  2. detailPage:展示一个问题的具体描述
  3. resultsPage:展示具体的结果
  4. votePage:具体投票的界面

增加几个视图

编辑polls.views.py文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)


def results(request, question_id):
response = "You're looking at the result of question %s."
return HttpResponse(response % question_id)


def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)

增加了视图,还要把视图的url写到polls.urls中:

1
2
3
4
5
6
7
8
9
urlpatterns = [
url(r'^$', views.index, name='index'),
# ex: /polls/5/
url(r'(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
# ex: /polls/5/results/
url(r'(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
# ex:/polls/5/vote/
url(r'(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]

Tips分别访问/pools/3/,polls/3/results/,polls/3/vote/会分别调用这几个模块视图,返回对应的结果.
稍微解释一下这个访问过程:当有个人访问127.0.0.1:8000/polls/3/这个链接的时候,Django会加载mysite.urls这个模块,因为这个文件是项目的根配置链接模块,然后在这个模块里找到了对应的匹配项^polls/,然后把3/根据include()函数找到对应的模块的urls配置文件polls.urls,最终调用对应的视图r'^P<question_id>[0-9]+)/$.调用视图的参数传递像下面这样:

1
detail(request=<HttpRequest object>, question_id='3')

这里的question_id='34'是从正则表达式(?P<question_id>[0-9]+)中来的,这个就是正则表达式里的一种用法,用括号括起来可以捕获正则匹配到的值,并且把他们作为参数传递给视图函数.其中?P<question_id>定义了这个匹配到的值的使用名,[0-9]+意思是匹配任意数字1次或者多次.

视图

上面的是一个简单的示例,其实这个视图根本没做什么事情,一般一个视图会做两件事:返回一个HttpResponse包含请求页面的内容,或者返回一个异常,例如Http404,这个取决于你.
为了让视图有内容,我们从数据库里读数据,将这些数据展示在网页上,例如显示最近的5条记录,修改polls/views.py

1
2
3
4
5
6
7
8
# Create your views here.
from models import Question


def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
output = ', '.join([q.quetion_text for q in latest_question_list])
return HttpResponse(output)

这里又有一个问题了,一般的网页设计都是写死的,如果你想改变网页的展示方式,你就要取改对应的Python代码,所以Django采用了设计和数据分离的模板模式,下面讲一讲如何使用模板来展示数据.

网页模板

首先在polls目录下创建一个templates目录,Django会到这个目录里找模板.在mysite/settings.py文件里,有如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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',
],
},
},
]

看到'APP_DIRS': True,这个会在我们刚创建的templates目录下扫描你已经安装的APP的目录里的模板.以我们这个例子为例,我们创建的模板的相对路径为:polls/templates/polls/index.html.当你使用的时候,直接就是polls/index.html.不要觉得这个麻烦,为什么要多创建一个polls的子目录而不是把模板文件直接放在templates下,这个就涉及到一个模板的查找个加载,Django默认从templates下查找,如果找到了就匹配第一个,所以不要省略子目录polls,你可以把这个看成是Java里的包的路径或者C++里的命名空间.
编辑polls/templates/pools/index.html

1
2
3
4
5
6
7
8
9
10
11
12
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id}}">
{{ question.question_text}}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

然后更新视图文件polls/views.py:

1
2
3
4
5
6
7
8
9
10
11
12
# Create your views here.
from django.template import loader
from models import Question


def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))

这段代码加载了一个模板polls/index.html,并且给模板传递了一个上下文,这是个字典.启动服务器,访问对应的网址127.0.0.1:8000/polls/默认会调用index()视图函数,返回一个列表,就是数据库里查询出来的几条记录.
Django对于这种常用的模板操作,也提供了一个简便的用法,我们来重写一下index()方法:

1
2
3
4
5
6
7
8
9
10
11
# Create your views here.
from django.shortcuts import render
from models import Question


def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {
'latest_question_list': latest_question_list,
}
return render(request, 'polls/index.html', context)

**render()**函数接收三个参数,第一个是一个request对象,第二个是一个模板,第三个是一个可选参数,字典内容.它返回一个渲染过的模板过后的HttpResponse对象以及字典内容

返回404错误

现在我们来处理一下详情detail()函数,编辑polls/views.py:

1
2
3
4
5
6
7
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoseNotExists:
raise Http404("Question does not exist")

return render(request, 'polls/detail.html', {'question': question})

然后在polls/detail.html中添加简单的内容

1
2
<p>Hello</p>
{{ question }}

同理,调用对应的方法,如果数据有,则会调用模板,没有则会跑出异常.

get_object_or_404()

由于我们经常使用这个用法,即有就获取内容,没有就抛出异常,所以Django也有一个简单的用法,重写detail()方法:

1
2
3
4
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)

return render(request, 'polls/detail.html', {'question': question})

这个会自动捕获object doesn't exist这个异常,还有个类似的方法get_list_or_404(),在列表是空的时候会抛异常.

使用模板系统

编辑模板文件夹下的polls/detail.html文件:

1
2
3
4
5
6
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

移除模板里的硬编码,让模板可以动态的显示内容,编辑polls/index.html:
把如下的内容:

1
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

改为:

1
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

先说一下为什么要这么改,上面一种写法为什么不好,其实可以想象,如果连接都写死,一旦我们的模板多起来了,改链接会很麻烦.其实有个简单的方法,在前面urls.py里,我们在写正则匹配url的时候,还定义了模板的名字,所以你可以使用{% url %}这个模板标签,这个是根据下面的定义来的:

1
2
3
4
...
# the 'name' value as called by the {% url %} template tag
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...

这样当你改了外部访问链接,例如127.0.0.1:8000/polls/specifics/12/,你只用在polls/url.py里改对应的正则规则就行,模板里的代码不用改.

URL命名空间

虽然上面的方法可以让我们少改代码,但是试想一下,一个真正的工程不可能只有一个APP模块,Django怎么知道在找到多个的时候用那个,答案是命名空间,是的,就是C++里的那个命名空间,我们来修改polls/urls.py文件:

1
2
3
4
app_name = 'polls'
urlpatterns = [
......
]

然后修改polls/index.html文件,将

1
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

修改为:

1
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

如果这样直接运行,然后访问127.0.0.1:8000/polls/会报错:

1
u'polls' is not a registered namespace

解决办法是在project的urls.pyinclude()的时候加上namespace属性,编辑mysite/urls.py文件:

1
2
3
4
urlpatterns = [
url(r'^polls/', include('polls.urls', namespace='polls')),
url(r'^admin/', admin.site.urls),
]

然后就可以访问了.

上一篇文章简单的搭建了一个Django的应用,互联网的应用,最后的数据都必须存储在数据库里,所以一个Web应用不可能不用到数据库,今天简单介绍一下Django中如何使用和配置数据库。

数据库设置

打开配置文件mysite/settings.py,默认这个配置文件是使用的SQLite数据库,如果只是简单的学些这个框架,做一些简单的应用,这个数据库很方便,是Python内置的,你不用再安装任何其他的驱动,包之类的。当然,如果你想使用更牛逼的数据库,例如MySQLPostgreSQL,看配置文件的下面的内容:

1
2
3
4
5
6
7
8
9
# Database
# https://docs.djangoproject.com/en/dev/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

ENGINE:

1
2
3
4
'django.db.backends.sqlite3',
'django.db.backends.postgresql',
'django.db.backends.mysql',
'django.db.backends.oracle',

NAME:你所创建的数据库文件的地址,记得是绝对路径,默认的是os.path.join(BASE_DIR,'db.sqlite3')
如果使用的是其他的数据库,还需要数据库的用户名和密码,记得一定要安装对应的驱动,例如,使用MySQL要安装类似于python-mysqldb这样的驱动,配置文件如下:

1
2
3
4
5
6
7
8
9
10
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test', #你的数据库名称
'USER': 'root', #你的数据库用户名
'PASSWORD': '', #你的数据库密码
'HOST': '', #你的数据库主机,留空默认为localhost
'PORT': '3306', #你的数据库端口
}
}

由于只是练手,简单的就是用默认配置了,在使用数据库之前,需要创建表,在终端里执行以下命令:

1
python manage.py migrate

这个命令其实是会根据mysite/settings.py里安装的模块创建对应的表,如果某些模块需要使用数据库,它就会创建对应的表,这么看实在是强大又方便。

1
2
3
4
5
6
7
8
9
10
# Application definition

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

看看执行命令的输出确实是这样的,有四个模块使用了数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Operations to perform:
Apply all migrations: admin, contenttypes, auth, sessions
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OK

实体类设计

在我们之前创建的一个应用里,我们来创建两个实体类:QuestionChoice,每个Choice都关联着一个Question。编辑polls/models.py文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from __future__ import unicode_literals

from django.db import models


# Create your models here.

class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')


class Choice(models.Model):
quetion = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)

**NOTE:**每个实体类有两个字段属性,注意Choice里的question不是字段,只是定义一个外键。

激活模块

现在我们已经创建了模块,剩下的我们要告诉我们的工程,polls这个应用要被安装,就是上面我们提到的配置文件,修改mysite/settings.py,改为以下内容:

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

然后在终端里运行下面的命令:

1
python manage.py makemigrations polls

将会看到如下输出:

1
2
3
4
5
Migrations for 'polls':
polls/migrations/0001_initial.py:
- Create model Choice
- Create model Question
- Add field quetion to choice

运行上面的命令的意思就是说,你已经修改了你的实体类,然后想把这些改动存储到数据库里。关于这个命令目前不需要知道太详细,知道这样用就可以了,但这样还不够,还需要运行下面的命令来把这些操作生效:

1
python manage.py migrate

看到如下输出就说明成功了:

1
2
3
4
5
Operations to perform:
Apply all migrations: admin, contenttypes, polls, auth, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK

上面的命令是把改变写到数据库中,使之生效,如果不运行这个,会报错,找不到表。

使用API交互调试

上回忘了说,manage.py有很多命令,其中有一个是交互式调试工具manage.py shell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
In [1]: import django

In [2]: django.setup()

In [3]: from polls.models import Question,Choice

In [4]: Question.objects.all()
Out[4]: <QuerySet []>

# 下面我们来插入一条记录,稍微多说一句,使用`timezone.now()`来替代
# datetime.datetime.now()
In [5]: from django.utils import timezone

In [6]: q = Question(question_text="What's new?", pub_date=timezone.now())
# 把这条记录存到数据库里
In [7]: q.save()

# 存到数据库之后这条记录就有id了
In [8]: q.id
Out[8]: 1

In [9]: q.question_text
Out[9]: "What's new?"

In [10]: q.pub_date
Out[10]: datetime.datetime(2016, 1, 3, 10, 4, 54, 39966, tzinfo=<UTC>)

In [11]: Question.objects.all()
Out[11]: <QuerySet [<Question: Question object>]>

看最后一行,是不是觉得根本看不出什么东西?类比于Java里,我们知道,有些类都会自带一个toString()方法,可以把一个类作为一个字符串输出来,这里我们也来定义一下类的toString方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')

def __str__(self):
return self.question_text


class Choice(models.Model):
quetion = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)

def __str__(self):
return self.choice_text

然后你需要退出交互式Python环境,重新进一边,就可以看到变化了:

1
2
In [4]: Question.objects.all()
Out[4]: <QuerySet [<Question: What's new?>]>

再给Question加一个比较常用的方法:

1
2
3
4
5
6
7
8
9
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')

def __str__(self):
return self.question_text

def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

使用admin模块

这个模块主要是管理员模块,管理网站的各种权限

1
python manage.py createsuperuser

后面照着填就行了。开启服务器:python manage.py runserver,访问http://127.0.0.1:8000/admin/
登陆进去之后界面大概是这样的:
Django 后台管理模块

导入模块到后台管理

编辑polls/admin.py文件,内容如下:

1
2
3
4
5
6
from django.contrib import admin

# Register your models here.
from models import Question

admin.site.register(Question)

把模块注册到后台管理模块中之后,就可以在管理模块中操作我们的数据了,再次刷新就可以看到我们的模块了:
polls模块
还可以添加管理数据:
后台管理数据库数据
这些数据会根据自己的类型选择自己的展示方式,日期的会有一个日历展示框。

0%