panda 2015-04-06T14:49:25+00:00 kejinlu@gmail.com Android自动化5_再述模拟键盘鼠标事件adb_shell_实现 2015-04-06T00:00:00+00:00 panda http://yujialive.github.io/android-automation-5 上一篇博文中讲述了通过Socket编程从外部向Emulator发送键盘鼠标模拟事件,貌似实现细节有点复杂。其实Android还有一种更简单的模拟键盘鼠标事件的方法,那就是通过使用adb shell 命令。

  1. 发送键盘事件: 命令格式1:adb shell input keyevent “value” 其中value以及对应的key code如下表所列:

在SDK2.1的模拟器中命令失效,sendevent命令可行

命令格式2:adb shell sendevent [device] [type] [code] [value] 如:

adb shell sendevent /dev/input/event0 1 229 1 代表按下按下menu键
adb shell sendevent /dev/input/event0 1 229 0 代表按下松开menu键

说明:上述的命令需组合使用 另外所知道的命令如下:

Key Name                       CODE
MENU                           229
HOME                           102
BACK (back button)             158
CALL (call button)             231
END (end call button)          107
  1. 发送鼠标事件(Touch): 命令格式:adb shell sendevent [device] [type] [code] [value]

情况1:在某坐标点上touch 如在屏幕的x坐标为40,y坐标为210的点上touch一下,命令如下

adb shell sendevent /dev/input/event0 3 0 40
adb shell sendevent /dev/input/event0 3 1 210

adb shell sendevent /dev/input/event0 1 330 1 //touch
adb shell sendevent /dev/input/event0 0 0 0       //it must have

adb shell sendevent /dev/input/event0 1 330 0 //untouch
adb shell sendevent /dev/input/event0 0 0 0 //it must have

注:以上六组命令必须配合使用,缺一不可

情况2: 模拟滑动轨迹(可下载并采用aPaint软件进行试验) 如下例是在aPaint软件上画出一条开始于(100,200),止于(108,200)的水平直线

adb shell sendevent /dev/input/event0 3 0 100 //start from point (100,200)
adb shell sendevent /dev/input/event0 3 1 200

adb shell sendevent /dev/input/event0 1 330 1 //touch
adb shell sendevent /dev/input/event0 0 0 0

adb shell sendevent /dev/input/event0 3 0 101 //step to point (101,200)
adb shell sendevent /dev/input/event0 0 0 0     //must list each step, here just skip
adb shell sendevent /dev/input/event0 3 0 108 //end point(108,200)
adb shell sendevent /dev/input/event0 0 0 0

adb shell sendevent /dev/input/event0 1 330 0 //untouch
adb shell sendevent /dev/input/event0 0 0 0
]]>
Android自动化4_模拟键盘鼠标事件_Socket_Instrumentation实现 2015-04-06T00:00:00+00:00 panda http://yujialive.github.io/android-automation-4 通过Socket + Instrumentation实现模拟键盘鼠标事件主要通过以下三个部分组成:

Socket编程:

实现PC和Emulator通讯,并进行循环监听

Service服务:

将Socket的监听程序放在Service中,从而达到后台运行的目的。这里要说明的是启动服务有两种方式,bindService和startService,两者的区别是,前者会使启动的Service随着启动Service的Activity的消亡而消亡,而startService则不会这样,除非显式调用stopService,否则一直会在后台运行因为Service需要通过一个Activity来进行启动,所以采用startService更适合当前的情形

Instrumentation发送键盘鼠标事件:

Instrumentation提供了丰富的以send开头的函数接口来实现模拟键盘鼠标,

如下所述:

sendCharacterSync(int keyCode)            //用于发送指定KeyCode的按键
sendKeyDownUpSync(int key)                //用于发送指定KeyCode的按键
sendPointerSync(MotionEvent event)     //用于模拟Touch
sendStringSync(String text)                   //用于发送字符串

注意:以上函数必须通过Message的形式抛到Message队列中。如果直接进行调用加会导致程序崩溃。

对于Socket编程和Service网上有很多成功的范例,此文不再累述,下面着重介绍一下发送键盘鼠标模拟事件的代码: 1. 发送键盘KeyCode

步骤1. 声明类handler变量

private static Handler handler;

步骤2. 循环处理Message

//在Activity的onCreate方法中对下列函数进行调用
private void createMessageHandleThread(){
    //need start a thread to raise looper, otherwise it will be blocked
        Thread t = new Thread() {
            public void run() {
                Log.i( TAG,"Creating handler ..." );
                Looper.prepare();
                handler = new Handler(){
                    public void handleMessage(Message msg) {
                           //process incoming messages here
                    }
                };
                Looper.loop();
                Log.i( TAG, "Looper thread ends" );
            }
        };
        t.start();
}

步骤3. 在接收到Socket中的传递信息后抛出Message

handler.post( new Runnable() {
            public void run() {
Instrumentation inst=new Instrumentation();
inst.sendKeyDownUpSync(keyCode);
}
} );
  1. Touch指定坐标,如下例子即touch point(240,400)

CODE

Instrumentation inst=new Instrumentation();
inst.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 240, 400, 0));
inst.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 240, 400, 0));
  1. 模拟滑动轨迹

将上述方法中间添加 MotionEvent.ACTION_MOVE

]]>
Android自动化3_架构实现 2015-04-06T00:00:00+00:00 panda http://yujialive.github.io/android-automation-3 前两节讲了用Android SDK自带的tool-hierarchyviewer来捕获Activity上Element,并分析了其中的原理。对于要实现GUI自动化,还有哪些工作没有完成呢?

  1. Invoke界面上的Element,如点击按钮,在文本框中输入内容等
  2. Press手机自身所有的按键,如HOME键,Menu键,左右上下方向键,通话键,挂机键等
  3. 判断测试结果

前面说过,直接从Emulator内部获取当前Activity上的Element这条路已经断了,同理,探索像UI Automation上一样Invoke Element的操作估计是行不通了,因为你拿不到Element的对象实例,所以实例所支持的方法当然也没有办法拿到。

怎么办?实在不行,基于坐标来对Element进行触发总可以吧。在Windows中发送基于坐标发送键盘和鼠标事件一般是在无法识别Element的情况下,想的最后一招,这使我想起起了Android中的monkey测试,对着屏幕就是一通乱点,压根就不管点的是什么。所幸的是,当前Android系统中我们得到了Element的属性信息,其中就包括坐标信息,而且这种信息是具有弹性的,也就是说即使Element的坐标随着开发的改变而有所变化,也不用担心,因为当前的坐标是实时获得的。

那么怎样才能给Element发送模拟按键等操作呢?总不能用Windows当前的键盘和鼠标事件吧,那样一旦模拟器的位置改变或失去焦点,啥都白搭,风险太大了。看来给Emulator内部发送模拟按键等操作比较靠谱。查了一下SDK,其中确实有这样的方法存在,但是我们当前的测试基础架构程序位于Emulator外部,怎么办?突然想起了hierarchyviewer的实现机制,通过Socket来发送信息。Hierarchyviewer有系统自带的进程给予答复响应(具体是哪个进程进行的响应不清楚,没有研究过)。那么我们也来模拟做一个Listener总可以吧。

其实对于模拟按键发送,网上的帖子很多,但大部分是基于一种方式实现的,IWindowManager接口。不巧的是,SDK并没有将该接口提供为public,所以需要用android源码替代android.jar包进行编译的方式进行绕行,感觉方法有点复杂。在后面另一篇系列文章中我会列出我在网上看到的另一种基于Instrumentation和MessageQueue的机制实现方法。

最后就剩下判断测试结果了。判断测试结果一般分为如下两种:外部条件是否满足,如文件是否产生,数据是否生成等;内部条件是否满足,如对应的Element是否出现或消失,Element上内容如字符串是否有变化。对于前一种本文不予讨论,后一种情况下,Element出现或消失可以通过hierarchyviewer来获取。仔细研究过hierarchyviewer会发现,它并没有提供Element界面上内容(Text)的属性。这可有点晕了,好像又要回到实现捕获Activity实例的老路上来了。考虑图像识别?这好像不靠谱。突然想到,4939端口上发送DUMP命令后的返回结果中会不会有此类hierarchyviewer没有显示出来的信息呢,万幸,还真有。在我上一篇博文(Hierarchyviewer 捕获Element的实现原理)中查询mText字段,会发现mText=1,5这样的信息,其实就是代表了计算器Button5上显示的内容5,逗号前的1表示后跟一位信息。

至此,问题似乎都解决掉了。画个基础架构图做个总结:

]]>
Android自动化2_Hierarchyviewer捕获Element的实现原理 2015-04-06T00:00:00+00:00 panda http://yujialive.github.io/android-automation-2 Android SDK tools下的工具hierarchyviewer可以展现Device上的Element的层次分布和自身属性,其核心函数之一就是LoadScene,研究后发现其实现方法是向Device的4939端口通过socket的方式发送了一个DUMP的命令,Device会自动处理该命令并将所有Screen上的Element层次结构和属性一并发回,实现代码如下:

public static void listElement(IDevice device) {
    Socket socket;
    BufferedReader in;
    BufferedWriter out;
    socket = null;
    in = null;
    out = null;
   
    do {
        DeviceBridge.setupDeviceForward(device);
    } while(4939!=DeviceBridge.getDeviceLocalPort(device));
   
    socket = new Socket();
    try {

        socket.connect(new InetSocketAddress("127.0.0.1", DeviceBridge.getDeviceLocalPort(device)));
        out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
         
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                   
        System.out.println("==> DUMP");
        out.write((new StringBuilder()).append("DUMP -1").toString());
                   
        out.newLine();
        out.flush();
        do {
            String line;
            if ((line = in.readLine()) == null || "DONE.".equalsIgnoreCase(line))
                break;
            line = line.trim();
            System.out.println(line);
        } while (true);

    } catch (IOException e) {
        e.printStackTrace();
    }
}

运行后的结果摘录其中一部分(button5),列举如下。注:当前device中运行的是2.1SDK中自带的Calculator程序:

com.android.calculator2.ColorButton@43b8bee8 mText=1,5 getEllipsize()=4,null mMinWidth=1,0 mMinHeight=1,0 mMeasuredWidth=2,79 mPaddingBottom=1,0 mPaddingLeft=1,0 mPaddingRight=1,0 mPaddingTop=1,0 mMeasuredHeight=2,78 mLeft=2,81 mPrivateFlags_DRAWING_CACHE_INVALID=3,0x0 mPrivateFlags_DRAWN=4,0x20 mPrivateFlags=8,16779312 mID=9,id/digit5 mRight=3,160 mScrollX=1,0 mScrollY=1,0 mTop=1,0 mBottom=2,78 mUserPaddingBottom=1,0 mUserPaddingRight=1,0 mViewFlags=9,402669569 getBaseline()=2,54 getHeight()=2,78 layout_gravity=4,NONE layout_weight=3,1.0 layout_bottomMargin=1,0 layout_leftMargin=1,1 layout_rightMargin=1,0 layout_topMargin=1,0 layout_height=11,FILL_PARENT layout_width=11,FILL_PARENT getTag()=4,null getVisibility()=7,VISIBLE getWidth()=2,79 hasFocus()=5,false isClickable()=4,true isDrawingCacheEnabled()=5,false isEnabled()=4,true isFocusable()=4,true isFocusableInTouchMode()=5,false isFocused()=5,false isHapticFeedbackEnabled()=4,true isInTouchMode()=4,true isOpaque()=5,false isSelected()=5,false isSoundEffectsEnabled()=4,true willNotCacheDrawing()=5,false willNotDraw()=5,false

另外还支持如下命令:

- LIST will show the list of windows:
LIST
43514758 com.android.launcher/com.android.launcher.Launcher
4359e4d0 TrackingView
435b00a0 StatusBarExpanded
43463710 StatusBar
43484c58 Keyguard
DONE.

END

]]>
Android自动化1_捕获Activity上的Element 2015-04-06T00:00:00+00:00 panda http://yujialive.github.io/android-automation-1 第一部分:前言

SDK中android.test等命名空间下的内容进行,但是Android系统下应用程序的测试现在应该还算是个新的领域,网上关于这方面的资料很多都是基于白盒测试的,一般都是基于JUnit框架和Android有一个前提,那就是必须要有应用程序的源代码以提供测试接入点,但是这在很多软件公司中是不现实的。很多测试工程师做的工作是完全黑盒,基本接触不到源代码,白盒测试大部分也是由开发自己完成。

回顾一下Windows下的黑盒测试自动化,先前使用的是微软提供的基于.net framework的UI Automation自动化测试框架(要求版本在.net framework 3.0以上,即VS.NET 2008 开发环境),对与擅长C#语言的人来说,使用起来确认比较好用。本人也写了基于UI Automation的轻量级的自动化框架,将在以后的博文中引出。

那在Android操作系统中能不能做类似于UI Automation的事情呢?不幸的是,Android的权限控制分的非常清楚,不同程序之间的数据访问只能通过Intent,content provider类似的功能实现。也就是说你开发的运行在Android中的自动化程序想要捕获当前运行的AUT (Application under Test) 界面上的控件等Element(该术语引自UI Automation,觉得翻译成元素有点生硬,故不作翻译)基本不可能,你也拿不到当前active activity的引用(截止本文发帖为止,个人暂时没有找到办法获得此引用)。

无路可走了?模拟器里面不能走,外面能不能走?或许可以。

第二部分:捕获Activity上的Element

在Android的SDK中自带了一个对自动化测试比较有用的工具:hierarchyviewer(位于SDK的tools目录下)。在模拟器运行的情况下,使用该工具可以将当前的Activity上的Element以对象树的形式展现出来,每个Element所含的属性也能一一尽显。这有点像Windows上运行的UI SPY,唯一遗憾的是不支持事件的触发。不过没有关系,可以想办法绕,当务之急是能在自行编写的自动化测试代码里找到Activity上的Element。

第一个想到的办法就是看hierarchyviewer源码,不巧,网上搜了一下,没有资源。或许Google的官网上有,但是上不去。看来只能反编译了,找来XJad,暴力之。虽然反编译出来的代码很多地方提示缺少import,但代码基本上是正确的。看了一下,确实也知道了许多。后来在编写代码的过程中,确实也证明了如果想引用hierarchyviewer.jar这个包并调试,还是需要知道里面的一些设置的。

创建基于hierarchyviewer.jar这个包的调用,需要将它和另外两个包,ddmlib.jar(在hierarchyviewer.jar同级目录中有)和org-netbeans-api-visual.jar(需要下载并安装netbeans,在其安装目录中有)一并导入到工程项目中,因为hierarchyviewer的实现依附于这两个包。

想在代码中获取Activity上的Element需要进行如下几个步骤(如果使用过hierarchyviewer这个工具后会发现,自动化代码所要写的就是该工具上的使用步骤)

  1. Ensure adb running
  2. Set adb location(因为hierarchyviewer和模拟器的沟通完全是依靠adb做的,所以设定正确的adb程序的位置至关重要,本人就曾在这个问题上栽了半天多)
  3. Get Active Device (这个等同动作发生在启动hierarchyviewer工具时)
  4. Start View Server (等同于工具上Start Server 菜单触发事件)
  5. Load Scene (等同于工具上Load View Hierarchy菜单触发事件)
  6. Get Root View Node (获得对象树的根节点,这个动作在工具上点击Load View Hierarchy菜单后会自动加载)
  7. Get Sub View Node (获得想要查找的View Node,这个动作在工具上点击Load View Hierarchy菜单后会自动加载)

说明:上述步骤中一些名称实际上就是hierarchyviewer中所提供的可访问方法名称,如startViewServer、loadScene、rootNode等。另外View Node实际上hierarchyviewer中的一个类,表示的对象树上的一个Element。

现将部分核心代码粘贴如下:

  1. Set adb location

CODE

System.setProperty("hierarchyviewer.adb","E:/Android/android-sdk-windows/tools");
其中"hierarchyviewer.adb" 这个key是hierarchyviewer.jar中指定的,后面的value是存放Android SDK的路径。这个目录必须是当前运行的模拟器所对应的adb的目录,不能自行使用其他目录下adb,否则会发生adb进程异常退出的错误。
  1. Get Active Device

CODE

IDevice[] devices = null;
DeviceBridge.terminate();
while(null==devices || 0==devices.length){
    DeviceBridge.initDebugBridge();
    //it must wait for some time, otherwise will throw exception
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    devices = DeviceBridge.getDevices();
}
return devices;

以上方法返回的是所有当前运行的Device列表

  1. Start View Server

CODE

DeviceBridge.startViewServer(device);
  1. Load Scene

CODE

ViewHierarchyLoader.loadScene(device, Window.FOCUSED_WINDOW) ;
  1. Get Root View Node

CODE

vhs.getRoot();
其中vhs是ViewHierarchyScene的实例对象
  1. Get Sub View Node

CODE

public ViewNode findFirstChildrenElement(IDevice device, 
            ViewNode entryViewNode, String elementID) {
    ViewNode node=null;
    if(0!=entryViewNode.children.size()) {
          for(int i=0;i<entryViewNode.children.size();i++) {
                node=entryViewNode.children.get(i);
                if(node.id==elementID)
                    return node;
                else
                    continue;
          }
    }
    return node;
}

虽然上述步骤所涉及的代码量不多,但是花了一天多的时间才终究研究出来,写下此文希望对正在研究Android自动化测试的同学们有些帮助。 到目前为止,Element已经得到了,接下去就是实现怎么去触发这些Element,如click button,enter text等等,尚需再慢慢研究,等有结果再贴出来分享!

]]>
Redmine 安装相关 2014-06-24T00:00:00+00:00 panda http://yujialive.github.io/redmine-install 安装系统相关类库组件
$ sudo apt-get install wget vim build-essential 
  openssl libreadline6 libreadline6-dev curl git-core 
  zlib1g zlib1g-dev libssl-dev libyaml-dev libxml2-dev 
  libxslt-dev autoconf automake libtool imagemagick libpcre3-dev 

install ruby on rails

如何快速正确的安装 Ruby, Rails 运行环境

link: https://ruby-china.org/wiki/install_ruby_guide

以下代码区域,带有 $ 打头的表示需要在控制台(终端)下面执行(不包括 $ 符号)

$ sudo apt-get install curl

# 安装 RVM
$ curl -L https://get.rvm.io | bash -s stable

# 然后,载入 RVM 环境(新开 Termal 就不用这么做了,会自动重新载入的)
$ source ~/.rvm/scripts/rvm

$ rvm -v
$ rvm install 2.0.0
# 同样继续等待漫长的下载,编译过程,完成以后,Ruby, Ruby Gems 就安装好了。

# RVM 装好以后,需要执行下面的命令将指定版本的 Ruby 设置为系统默认版本
$ rvm 2.0.0 --default

$ ruby -v
$ gem -v

# update source
$ gem source -r https://rubygems.org/
$ gem source -a https://ruby.taobao.org

# 上面 3 个步骤过后,Ruby 环境就安装好了,接下来安装 Rails
$ gem install rails -v '3.2.17'

# 然后测试安装是否正确
$ rails -v

install mysql

link: https://help.ubuntu.com/10.04/serverguide/mysql.html

# Installation
$ sudo apt-get install mysql-server

# -u root -p root
$ mysql -u root -p
> show databases;

$ sudo netstat -tap | grep mysql

# When you run this command, you should see the following line or something similar:
$ tcp        0      0 localhost:mysql         *:*                     LISTEN      2556/mysqld

# If the server is not running correctly, you can type the following command to start it:
$ sudo /etc/init.d/mysql restart

# Configuration

# You can edit the /etc/mysql/my.cnf file to configure the basic settings 
# -- log file, port number, etc. For example, to configure MySQL to listen for
# connections from network hosts, change the bind-address directive to the server's IP address:

bind-address = 192.168.0.5

# After making a change to /etc/mysql/my.cnf the mysql daemon will need to be restarted:

$ sudo /etc/init.d/mysql restart

$ sudo apt-get install libmysqlclient-dev
$ sudo dpkg -L libmysqlclient-dev|grep config

> grant all on redmine.* to 'redmine'@'localhost';
> flush privileges;
* install redmine

Download redmine

link: http://www.redmine.org/projects/redmine/wiki/RedmineInstall

$ tar -xvf redmine-2.5.1.tar.gz 
basedir: ~/redmine/redmine-2.5.1

Copy config/database.yml.example to config/database.yml

# edit 
production:
  adapter: mysql2
  database: redmine
  host: localhost
  port: 3307
  username: redmine
  password: 'my_password'

Dependencies installation

# You need to install Bundler first
$ gem install bundler

# cd to redmine dir
$ cd ~/redmine/redmine-2.5.1

# Then you can install all the gems required by Redmine using the following command
$ bundle install --without development test

# If ImageMagick is not installed on your system, you should skip the installation of the rmagick gem using:
$ bundle install --without development test rmagick

# troubleshooting for install mysql2 failed
link: http://rubydoc.info/gems/mysql2/frames

$ gem install mysql2 -- --with-mysql-dir=/usr/share/mysql

# Session store secret generation
$ rake generate_secret_token

$ RAILS_ENV=production rake db:migrate

$ RAILS_ENV=production rake redmine:load_default_data

http://www.2cto.com/os/201207/144189.html

]]>
手机网络制式常识 2014-06-10T00:00:00+00:00 panda http://yujialive.github.io/phone-model 手机网络制式常识:

手机名称有GSM:表示只支持中国联通或者中国移动2G号段(130、131、132、134、135、136、137、138、139、145、147、150、151、152、155、156、157、158、159、182、183、185、186、187、188)

手机名称有CDMA:表示只支持中国电信2G号段(133、153、180、181、189)

手机名称有WCDMA/GSM:表示支持中国联通或者中国移动2G号段,以及中国联通3G号段(130、131、132、134、135、136、137、138、139、145、147、150、151、152、155、156、157、158、159、182、183、185、186、187、188、1709),不支持移动3G业务,不支持电信卡。

手机名称有TD-SCDMA/GSM:表示支持中国联通或者中国移动2G号段,以及中国移动3G号段(130、131、132、134、135、136、137、138、139、145、147、150、151、152、155、156、157、158、159、182、183、185、186、187、188),不支持联通3G业务,不支持电信卡。

手机名称有CDMA2000/CDMA:表示支持中国电信2G号段,以及中国电信3G号段(133、153、180、181、189、1700),不支持移动联通卡

手机名称有CDMA2000/GSM(双模双待):表示一张卡支持中国电信2G号段,以及中国电信3G号段(133、153、180、181、189、1700),另一张卡支持中国移动或中国联通2G号段的语音和短信功能。

]]>
Django框架 2014-05-28T00:00:00+00:00 panda http://yujialive.github.io/django1 Django

ref :https://docs.djangoproject.com/en/1.6/intro/tutorial01/

#in some dir
python django-admin.py startproject <pname>

    <pname>/
        manage.py
        <pname>/
            __init__.py
            settings.py
            urls.py
            wsgi.py

python manage.py startapp <aname>

    <aname>/
        __init__.py
        admin.py
        models.py
        tests.py
        views.py

python manage.py runserver
python manage.py runserver 8080

python manage.py syncdb

Projects vs. apps

What’s the difference between a project and an app? An app is a Web application that does something – e.g., a Weblog system, a database of public records or a simple poll app. A project is a collection of configuration and apps for a particular Web site. A project can contain multiple apps. An app can be in multiple projects.

Your apps can live anywhere on your Python path. In this tutorial, we’ll create our poll app right next to your manage.py

python manage.py sql <aname>

The sql command doesn’t actually run the SQL in your database - it just prints it to the screen so that you can see what SQL Django thinks is required. If you wanted to, you could copy and paste this SQL into your database prompt. However, as we will see shortly, Django provides an easier way of committing the SQL to the database.

If you’re interested, also run the following commands:

  • python manage.py validate – Checks for any errors in the construction of your models.
  • python manage.py sqlcustom polls – Outputs any custom SQL statements (such as table modifications or constraints) that are defined for the application.
  • python manage.py sqlclear polls – Outputs the necessary DROP TABLE statements for this app, according to which tables already exist in your database (if any).
  • python manage.py sqlindexes polls – Outputs the CREATE INDEX statements for this app.
  • python manage.py sqlall polls – A combination of all the SQL from the sql, sqlcustom, and sqlindexes commands.

Now, run syncdb again to create those model tables in your database:

python manage.py syncdb

The syncdb command runs the SQL from sqlall on your database for all apps in INSTALLED_APPS that don’t already exist in your database. This creates all the tables, initial data and indexes for any apps you’ve added to your project since the last time you ran syncdb. syncdb can be called as often as you like, and it will only ever create the tables that don’t exist.

django-admin.py runserver --settings=mysite.settings

python manage.py shell

We’re using this instead of simply typing “python”, because manage.py sets the DJANGO_SETTINGS_MODULE environment variable, which gives Django the Python import path to your mysite/settings.py file.

Bypassing manage.py
If you’d rather not use manage.py, no problem. Just set the DJANGO_SETTINGS_MODULE environment variable to mysite.settings and run python from the same directory manage.py is in (or ensure that directory is on the Python path, so that import mysite works).

>>> from polls.models import Poll, Choice   # Import the model classes we just wrote.

# No polls are in the system yet.
>>> Poll.objects.all()
[]

# Create a new Poll.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> p = Poll(question="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> p.save()

# Now it has an ID. Note that this might say "1L" instead of "1", depending
# on which database you're using. That's no biggie; it just means your
# database backend prefers to return integers as Python long integer
# objects.
>>> p.id
1

# Access database columns via Python attributes.
>>> p.question
"What's new?"
>>> p.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# Change values by changing the attributes, then calling save().
>>> p.question = "What's up?"
>>> p.save()

# objects.all() displays all the polls in the database.
>>> Poll.objects.all()
[<Poll: Poll object>]

unicode or str?

On Python 3, things are simpler, just use str() and forget about unicode().

If you’re familiar with Python 2, you might be in the habit of adding str() methods to your classes, not unicode() methods. We use unicode() here because Django models deal with Unicode by default. All data stored in your database is converted to Unicode when it’s returned.

Django models have a default str() method that calls unicode() and converts the result to a UTF-8 bytestring. This means that unicode(p) will return a Unicode string, and str(p) will return a normal string, with characters encoded as UTF-8.

If all of this is gibberish to you, just remember to add unicode() methods to your models. With any luck, things should Just Work for you.

import datetime
from django.utils import timezone
# ...
class Poll(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

Note the addition of import datetime and from django.utils import timezone, to reference Python’s standard datetime module and Django’s time-zone-related utilities in django.utils.timezone, respectively. If you aren’t familiar with time zone handling in Python, you can learn more in the time zone support docs.

Save these changes and start a new Python interactive shell by running python manage.py shell again:

>>> from polls.models import Poll, Choice

# Make sure our __unicode__() addition worked.
>>> Poll.objects.all()
[<Poll: What's up?>]

# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Poll.objects.filter(id=1)
[<Poll: What's up?>]
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]

# Get the poll that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Poll.objects.get(pub_date__year=current_year)
<Poll: What's up?>

# Request an ID that doesn't exist, this will raise an exception.
>>> Poll.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Poll matching query does not exist.

# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Poll.objects.get(id=1).
>>> Poll.objects.get(pk=1)
<Poll: What's up?>

# Make sure our custom method worked.
>>> p = Poll.objects.get(pk=1)
>>> p.was_published_recently()
True

# Give the Poll a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a poll's choices) which can be accessed via the API.
>>> p = Poll.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> p.choice_set.all()
[]

# Create three choices.
>>> p.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice objects have API access to their related Poll objects.
>>> c.poll
<Poll: What's up?>

# And vice versa: Poll objects get access to Choice objects.
>>> p.choice_set.all()
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
>>> p.choice_set.count()
3

# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any poll whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(poll__pub_date__year=current_year)
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]

# Let's delete one of the choices. Use delete() for that.
>>> c = p.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

Make the poll app modifiable in the admin But where’s our poll app? It’s not displayed on the admin index page.

Just one thing to do: we need to tell the admin that Poll objects have an admin interface. To do this, open the polls/admin.py file, and edit it to look like this:

from django.contrib import admin
from polls.models import Poll

admin.site.register(Poll)
]]>
Github SSH 设置 2014-05-27T00:00:00+00:00 panda http://yujialive.github.io/github-ssh
  • link: http://beiyuu.com/github-pages/
  • changing a remote url: https://help.github.com/articles/changing-a-remote-s-url
  • passphrases: https://help.github.com/articles/working-with-ssh-key-passphrases
  • open git bash to gen key

    ssh-keygen -T rsa -C "yujialive@gmail.com"
    passphrase # 可以为空格
    
    <homedir>/.ssh/id_rsa
    <homedir>/.ssh/id_rsa.pub

    copy pub key to github site

    id_rsa.pub to github.com -- user settings -- ssh keys

    test ssh and gen hosts

    ssh -T git@github.com

    config repo

    # change to ssh link for old repo
    git remote -v
    git remote set-url origin git@github.com:yujialive/yujialive.github.io.git
    
    # new git repo
    git remote add origin git@github.com:yujialive/yujialive.github.io.git
    
    git config --global user.name "yujialive"
    git config --global user.email "yujialive@gmail.com"
    
    # or
    git config --local user.name "yujialive"
    git config --local user.email "yujialive@gmail.com"
    ]]>
    Android activity launch mode 2014-05-09T00:00:00+00:00 panda http://yujialive.github.io/android-activity-launchmode 在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作。在Android中Activity的启动模式决定了Activity的启动运行方式。

    Activity启动模式设置

    <activity android:name=".MainActivity" android:launchMode="standard" />
    

    Activity的四种启动模式

    1. standard 默认启动模式,每次激活Activity时都会创建Activity,并放入任务栈中。

    2. singleTop 如果在任务的栈顶正好存在该Activity的实例, 就重用该实例,否者就会创建新的实例并放入栈顶(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例)。

    3. singleTask 如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent())。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移除栈。如果栈中不存在该实例,将会创建新的实例放入栈中。

    4. singleInstance 在一个新栈中创建该Activity实例,并让多个应用共享改栈中的该Activity实例。一旦改模式的Activity的实例存在于某个栈中,任何应用再激活改Activity时都会重用该栈中的实例,其效果相当于多个应用程序共享一个应用,不管谁激活该Activity都会进入同一个应用中。

    ]]>