月度归档:2015年11月

django站点Middleware错误的一种可能原因的解决

错误信息:

ImproperlyConfigured: Module "django.contrib.auth.middleware" does not define a "SessionAuthenticationMiddleware" attribute/class

错误原因:

django版本从1.6升级到1.8时产生的不兼容。在django 1.6中无法运行django 1.8的工程。

解决方法:

升级django版本到1.8或者在虚拟环境env中配置1.8环境执行。

Windows平台使用Virtualenv部署django环境并整合Eclipse PyDev插件说明

1、预先配置好的内容

1、Windows 7系统
2、Python 2.7
#没有的话在这里下载 https://www.python.org/downloads/release/python-2710/
3、pip
#没有的话在这里下载 https://pip.pypa.io/en/stable/installing/
4、创建好的django项目:djangosite
#或者新创建一个 django-admin startproject djangosite
5、配置好PyDev插件的Eclipse EE

2、安装配置Virtualenv

pip install virtualenv

进入djangosite目录,创建虚拟环境到目录env/

virtualenv env

之后在虚拟环境djangosite\env\中配置项目需要的环境

启用和关闭Virtualenv环境

#启动
C:\djangosite> env\Scripts\activate
(env) C:\djangosite>

#关闭
(env) C:\djangosite> env\Scripts\deactivate.bat

3、安装django

C:\djangosite> env\Scripts\activate
(env) C:\djangosite> pip install django

4、Eclipse整合env环境

在Eclipse中选择需要使用虚拟环境的工程 –> Properties

20151127111920

点击”Click here to configure an interpreter not listed.”

20151127112101

点击New按钮创建新的Interpreter,路径选择到env虚拟环境下的python.exe,重命名为python2.7.10

20151127112141

选择虚拟环境中的site-packages,保存。

20151127112213

这时退回到之前的页面就能看见新的Interpreter选项了,选择保存即可。

此时执行manage.py时使用的环境就已经是env中的虚拟环境了。

在Ubuntu服务器上部署django网站(Apache2)

基本环境部署:

~# apt-get install python-pip
~# pip install Django

Create project djangosite,  and start app helloworld

root@localhost:/srv/www# django-admin startproject djangosite
root@localhost:/srv/www# cd djangosite
root@localhost:/srv/www/djangosite# python manage.py startapp helloworld

修改helloworld应用目录下的view.py文件

root@localhost:/srv/www/djangosite/helloworld# vi views.py

#coding:utf-8
from django.http import HttpResponse
from django.shortcuts import render

def index(request):
    return HttpResponse(u"Hello World!")

新定义的app加到settings.py中的INSTALL_APPS中

root@localhost:/srv/www/djangosite# vi djangosite/settings.py

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

    'helloworld',
)

修改url.py定义访问路径

root@localhost:/srv/www/djangosite/djangosite# vi urls.py 

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

admin.autodiscover()

urlpatterns = [

    url(r'^$', 'helloworld.views.index', name = 'home'),
    url(r'^admin/', include(admin.site.urls)),
]

至此,django这边的配置结束,网站已经可以使用managy.py runserver运行,并通过127.0.0.1:8000的localhost访问。

下面配置Apache2使得网站能够通过域名进行外部访问。

root@localhost:/# vi /etc/apache2/sites-available/django.zivers.com.conf 

<VirtualHost *:80>
        ServerName django.zivers.com
        ServerAdmin webmaster@localhost
        DocumentRoot /srv/www/djangosite/

        WSGIScriptAlias / /srv/www/djangosite/djangosite/wsgi.py

        <Directory /srv/www/djangosite/djangosite>
        <Files wsgi.py>
                Require all granted
        </Files>
        </Directory>
</VirtualHost>

修改wsgi文件

root@localhost:/# vi /srv/www/djangosite/djangosite/wsgi.py

import os
from os.path import join, dirname, abspath

PROJECT_DIR = dirname(dirname(abspath(__file__)))
import sys
sys.path.insert(0, PROJECT_DIR)

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangosite.settings")

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

启用配置并重启Apache2

root@localhost:/srv/www/djangosite/djangosite# a2ensite django.zivers.com.conf
root@localhost:/srv/www/djangosite/djangosite# servcie apache2 reload

这时候网站已经能通过django.zivers.com正常访问

20151126102135

 

 

Linux(Ubuntu)SSH登录白名单设置

为了服务器安全,我们可以通过白名单的方法对服务器登录和SQL登录进行限制,仅让白名单上的IP通过而禁止其他用户登录SSH和SQL。具体原理为在iptables中加入Accept和Drop规则,仅让Accept的IP地址访问对应端口而Drop其他的请求。实现方法:

我们查看服务器初始的iptables

root@localhost:~# iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

初始的iptables没有任何规则。

在/etc/network/目录中找到if-up.d/  if-down.d/两个文件夹,向其中分别加入shell脚本,不妨命名为login_protection,在脚本中写入IP限制规则。下面给出样例:

#/etc/network/if-up.d/login_protect
#!/usr/bin/env bash
/sbin/iptables -A INPUT -s <YOUR_IP> -p tcp -m tcp --dport 22 -j ACCEPT
/sbin/iptables -A INPUT -s <YOUR_IP> -p tcp -m tcp --dport 3306 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -m tcp --dport 22 -j DROP
/sbin/iptables -A INPUT -p tcp -m tcp --dport 3306 -j DROP
#/etc/network/if-down.d/login_protect
#!/usr/bin/env bash
/sbin/iptables -D INPUT -s <YOUR_IP> -p tcp -m tcp --dport 22 -j ACCEPT
/sbin/iptables -D INPUT -s <YOUR_IP> -p tcp -m tcp --dport 3306 -j ACCEPT
/sbin/iptables -D INPUT -p tcp -m tcp --dport 22 -j DROP
/sbin/iptables -D INPUT -p tcp -m tcp --dport 3306 -j DROP

将这两个sh设置为可执行,然后重启network服务

~# chmod 755 /etc/network/if-up.d/bfa_protection
~# chmod 755 /etc/network/if-down.d/bfa_protection
~# /etc/init.d/networking restart

 

TwentyTwelve主题添加顶部搜索框

WordPress自带了一个叫做get_search_form()的方法,调用这个方法就能够显示一个搜索框,所以为了在顶部添加搜索框,我们就需要在header.php中调用这个方法,具体如下:

<hgroup>
    <h1 class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" style="font-family: Microsoft YaHei;font-weight: normal" rel="home"><?php bloginfo( 'name' ); ?></a></h1>
    <div class="headersearch"><?php get_search_form_header(); ?></div>
</hgroup>
    <h2 class="site-description">&nbsp-&nbsp<?php bloginfo( 'description' ); ?></h2>

这里把headersearch加入到了hgroup中,并把h2从hgroup中拿出,是为了让显示的时候能够使搜索框和站点标题在同一水平线上。

然后修改style.css,在底部加入以下内容:

.headersearch {
	float: right;
	padding: 25px 0 0 0;
	margin-right: 70px;
}

.site-header h1, .headersearch {
	display:inline-block;
	clear:none;
}

注意到,我在header.php中添加的方法是get_search_form_header()而不是原来的get_search_form()这是因为我对头部的搜索框进行了修改,不现实搜索按钮。这个方法添加在general-template.php中,具体为:

function get_search_form_header( $echo = true ) {
        
        do_action( 'pre_get_search_form' );

        $format = current_theme_supports( 'html5', 'search-form' ) ? 'html5' : 'xhtml';

        
        $format = apply_filters( 'search_form_format', $format );

        $search_form_template = locate_template( 'searchform.php' );
        if ( '' != $search_form_template ) {
                ob_start();
                require( $search_form_template );
                $form = ob_get_clean();
        } else {
                if ( 'html5' == $format ) {
                        $form = '<form role="search" method="get" class="search-form" action="' . esc_url( home_url( '/' ) ) . '">
                                <label>
                                        <span class="screen-reader-text">' . _x( 'Search for:', 'label' ) . '</span>
                                        <input type="search" class="search-field" placeholder="' . esc_attr_x( 'Search &hellip;', 'placeholder' ) . '" value="' . get_search_query() . '" name="s" title="' . esc_attr_x( 'Search for:', 'label' ) . '" />
                                </label>
                        </form>';
                } else {
                        $form = '<form role="search" method="get" id="searchform" class="searchform" action="' . esc_url( home_url( '/' ) ) . '">
                                <div>
                                        <label class="screen-reader-text" for="s">' . _x( 'Search for:', 'label' ) . '</label>
                                        <input type="text" value="' . get_search_query() . '" name="s" id="s" />
                                </div>
                        </form>';
                }
        }
        
        $result = apply_filters( 'get_search_form', $form );

        if ( null === $result )
                $result = $form;

        if ( $echo )
                echo $result;
        else
                return $result;
}

其实就是复制get_search_form()方法,删除其中的搜索按钮。

效果预览:

20151118095450

文件拷贝后如何保持Git版本控制一致

实际操作中遇到如下问题:在Github上有以下文件:

remote: a b c

使用git clone到本地

local : a b c

然后有以下操作,在本地将b重命名为d(先),在服务器上添加文件e(后)。

于是现在的情况变成了如下:

remote: a b c   e
local : a   c d 
#其中d是由b复制后修改而来

此时,如果在本地使用pull命令,将得到以下结果

remote: a b c   e
local : a   c d e

问题出现:b文件无法被拉取下来,因为服务器上的版本较低,系统认为是重命名操作,b文件并非被删除。而e文件显然被正常拉取

考虑到另一种情况,如果在本地使用的是push命令,则Github上的b文件将被删除并增加一个d文件。

那么如何才能在本地重新获得b文件呢?

答案是:git checkout

在本地执行checkout b,就能把本地仓库的b文件拉取出来:

git checkout b

最后,简单的回顾一下问题:

现状:

remote: a b c   e
local : a   c d

解决:

git pull origin master
git checkout b
git commit
git push origin master

结果:

Github: a b c d e
local : a b c d e

远程仓库和本地保持了一致。复制操作完成。

 

使用Google Search Analytics API获取页面访问信息实例(Python)

Google统计在最近修改了调取页面访问信息的API,这里给出一个应用实例。

截取API中关于页面统计部分(这里略去头部的Import内容)

argparser = argparse.ArgumentParser(add_help=False)
argparser.add_argument('property_uri', type=str,
                       help=('Site or app URI to query data for (including '
                             'trailing slash).'))
argparser.add_argument('start_date', type=str,
                       help=('Start date of the requested date range in '
                             'YYYY-MM-DD format.'))
argparser.add_argument('end_date', type=str,
                       help=('End date of the requested date range in '
                             'YYYY-MM-DD format.'))

def main(argv):
  service, flags = sample_tools.init(
      argv, 'webmasters', 'v3', __doc__, __file__, parents=[argparser],
      scope='https://www.googleapis.com/auth/webmasters.readonly')

  request = {
      'startDate': flags.start_date,
      'endDate': flags.end_date,
      'dimensions': ['page'],
      'rowLimit': 5000
  }
  response = execute_request(service, flags.property_uri, request)
  return response

def execute_request(service, property_uri, request):
  return service.searchanalytics().query(
      siteUrl=property_uri, body=request).execute()

if __name__ == '__main__':
  main(sys.argv)

从这里我们可以看到API首先给argparser这个对象添加了三个控制台参数:property_uri, start_date, end_date。即页面的URI和起止日期。所以要执行这个API需要提供三个参数,即如下:

python search_analytics_api.py http://blog.zivers.com 2015-10-01 2015-11-01

所以我们在调用这个接口时也需要添加以上参数。获取到这些参数后,组合成request的json请求发送至execute_request方法中执行,返回response字典。

接下来就是怎么调取这个API为我们获取数据了,这里我新建了一个GAReport类,这个类中封装了调取API的wm_pages方法,使用时只需调取这个类即可:

from google.search_analytics_api import main as ga_api

class GAReport():
  def wm_pages(self, requestPage):
    try:
      monthly_queries = {}
      pageinfo = ga_api(requestPage)
      monthly_queries = pageinfo
      return monthly_queries
    except ValueError:
      return {}
    
  def wm_page(self, requestUrl, requestPage):
    try:
      pageInfo = []
      rows = requestPage['rows']
      for row in rows:
        if row['keys'][0] == requestUrl:
          pageInfo = [row['clicks'], row['impressions'], row['ctr'], row['position']]
          return pageInfo
    except ValueError:
      return []

wm_pages()方法含有一个requestPage的list参数,这个参数中包含了API所需要的三个参数,调取API处理数据后,获得的response字典存入monthly_queries中。

wm_page()方法是为了查询特定页面的访问信息而写的。实现从API返回的字典结果中查询并返回特定URL的数据并格式化成list[[row[‘clicks’], row[‘impressions’], row[‘ctr’], row[‘position’]]]格式,返回pageInfo列表。

最后只需要在程序中实例化一个GAReport对象并提供参数即可:

startDate = start_date.strftime("%Y-%m-%d")
endDate = end_date.strftime("%Y-%m-%d")
requestPage = ['search_analytics_api.py', host, startDate, endDate]
gareport = GAReport()
gm_page_info_list = gareport.wm_pages(requestPage)
gm_page_info_request = gareport.wm_page(article_url, gm_page_info_list)

 

服务器从Vultr迁移至Aliyun札记

从开始搭建网站开始,我已经使用过了许多的主机服务:Godaddy虚拟主机(2011) => 西部数据虚拟主机(2012) => AWS(2013) => 阿里云虚拟主机(2014) => Vultr VPS/Linode VPS(2015)

从开始的虚拟主机,到之后的VPS,不同的服务带来的是不同的使用感受。最初使用的虚拟主机(Web Hosting Service)能够在短时间内架设CMS站点,十分方便,而所带来的代价就是失去了定制性和通用性,除了CMS,什么都做不了。而且即使是架设CMS也会因为无法修改.htaccess文件,缺少脚本组件,服务器流量和应用资源限制等各种各样的原因无法放开手脚。转而使用VPS之后,可以做的事情就大大增加了。无论是架设Shadowsocks, PPTP, LNMP都变得十分方便。这也是我在有了一定的技术准备之后转而使用VPS的原因。

在15年初,自己购买了Vutlr的主机服务,而公司使用的是Linode的服务器这让我能够同时感受这两者的区别。其实这两个公司的VPS的使用体验都是非常不错的,尤其是在选择日本的服务器中心时,在国内的Ping值最低可以达到60ms,平均也能在120左右。这对于网站的访问已经是一个不错的速度了。而且使用国外的VPS将得到一个额外的福利,你基本不用购买VPN服务了,在VPS上架设一个PPTP VPN类似的服务十分容易。

至于为何又转回国内,原因也很简单,价格便宜。Vutlr和Linode的基本服务器配置均为$10/mon(1GHz CPU, 1G RAM, SSD),年费¥700+。而使用国内的阿里云(1GHz CPU, 1G RAM),按流量计费一年固定配置¥450,年流量按100G计算流量费用也仅需¥50,总计不过500。加上双11的代金券和折扣,总共付费¥350,只有Linode的一半。使用国内的服务还能获得更快的国内访问速度,而网站备案也在14年注册阿里云虚拟主机的时候解决了。在没有备案这一最大障碍的情况下,转到国内的服务器上就变的水到渠成了。

最后说下网站的迁移,网站是用WordPress搭建的,所谓的服务器迁移不过是把文件和数据库迁移,然后重新配置就行了。

文件迁移:我是在原来的网站直接将站点整体打包成.tar,然后解压到当前站点网站目录。

数据库迁移:直接使用phpMyAdmin将数据库备份到本地,然后在新网站上也用phpMyAdmin恢复数据库。

文件迁移和数据库迁移完成后,我甚至都不需要修改配置文件,网站就已经能够正常使用了,这简直是给我的一个惊喜。

2015-11-11 @Henry