顯示具有 GAE 標籤的文章。 顯示所有文章
顯示具有 GAE 標籤的文章。 顯示所有文章

Google App Engine HTTP請求處理

教學範例4

RequestHandler物件

前面介紹過的資料模型(model)是負責儲存與讀取資料,範本引擎(template)則是用來顯示畫面。本篇文章要介紹的RequestHandler物件,是在webapp開發框架中用來處理HTTP請求的物件,它會產生適當的回應給用戶端。

在GAE的範例中,我們利用WSGIApplication來解析使用者傳送過來的HTTP請求,每一個URL網址會被導到一個相對應的webapp2.RequestHandler物件,當請求方法是Post時,就會用RequestHandler物件中的post方法處理,同理,當請求方法是Get時,就會利用RequestHandler物件中的get方法處理。

urls.py程式

import webapp2

from main import MainPage,Guestbook


application = webapp2.WSGIApplication([
    ('/', MainPage),
    ('/sign', Guestbook),
], debug=True)


main.py程式

.................
.................
class MainPage(webapp2.RequestHandler):
    def get(self):
...........................
...........................

class Guestbook(webapp2.RequestHandler):
    def post(self):
..........................
.........................



URL網址解析

在WSGIApplication物件中,有關URL網址對應解析規則,我們可以直接指明某一個URL要對應到哪一個RequestHandler物件或是利用正規表示式來指定對應的通則。


例如我們顯示文章的URL可能是/article/2013/07/18/10或/article/2014/08/15/4的規則,它所代表的意義是某一個日期所發表的文章,因此我們可以使用正規表示法來描述這類型的網址。

application = webapp2.WSGIApplication([
    ('/article/(\d{4})/(\d{2})/(\d{2})/(\d+)', Article),
], debug=True)


在正規表示法中被括號 "( " 及 " ) "包起來的部份,就表示在RequestHandler物件中會被當成參數傳送進來,因此我們可以在post或get方法中,設計適當的參數來接收傳進來的文字型態的值。

class Article(webapp2.RequestHandler):
    def get(self,year,month,day,aid):
          ........................
          .......................


你也可以運用不定參數個數的方式來取得傳送進來的值,如:
class Article(webapp2.RequestHandler):
    def get(self,*args):
          .......................
          year=args[0]
          month=args[1]
          ........................
          ........................
          ........................

請求物件

當用戶端透過網頁以表單(form)或在網址URI上使用查詢字串query string(如/article?year=2014&month=7)的方式傳送參數時,我們可以把它們視為請求參數,如果我們要在RequestHandler物件中取得這些參數值,就必須使用request物件的get方法才能取得。

import webapp2

class Article(webapp2.RequestHandler):
    def post(self):
          year=self.request.get('year')
          month=self.request.get('month')
          ................


在某些情況下,你可能會接收不到用戶端的值,或是參數不存在。你可以設定預設值來解決這種問題。如:

import webapp2

class Article(webapp2.RequestHandler):
    def post(self):
          year=self.request.get('year',default_value='2014')
          month=self.request.get('month')
          ................

在HTML的表單中,部份欄位型態資料是可以複選的,如<select ....... multiple="true">或相同name變數的<input type="checkbox">欄位,同一個參數可能會接收到多筆資料,此時我們就可以用request物件的get_all來取得一個list的值資料。如:

html 檔

<select name="selectCourse" multiple="true">
    <option value="chinese">chinese</option>
    <option value="english">english</option>
    <option value="math">math</option>
</select>
....


py檔

import webapp2

class Course(webapp2.RequestHandler):
    def post(self):
          lstSelectCourse=self.request.get_all('selectCourse')
          for strCourse in lstSelectCourse:
          ................


有關request物件相關屬性,可參考
https://developers.google.com/appengine/docs/python/tools/webapp/requestclass?hl=zh-tw

Google App Engine Template 引擎簡介

教學範例3

所謂的"template"在中文常翻譯成"範本",在Google App Engine上也是相同的概念。當程式執行完成時,會需要將產生的結果呈現在畫面上,此時就需要系統提供一個範本檔,讓程式依照該範本檔案來做輸出。

在範本檔案中會有template引擎的語法來告訴template引擎要在什麼地方填上什麼資料(可能是一個變數、迴圈或判斷等),程式只要利用template引擎將資料餵到範本檔案裡,就可以完成資料畫面的輸出了。

目前Google App Engine預設提供了Django template引擎,所以我們可以使用大部份django template引擎的語法,在使用時,需要先將google.appengine.ext.webapp套件引入。即:

from google.appengine.ext.webapp import template
..........
..........
       content=template.render('index.html',{})
       self.response.out.write(content)


上例是範本檔案中不需要輸出變數,則直接使用render方法帶入範本檔案路徑即可,若程式中需要將變數資料帶入範本中顯示時,則需要多加一個python dict資料結構來傳遞才可,如下所示:

from google.appengine.ext.webapp import template
............
............
      x=1
      y='abc'
      content=template.render('index.html',{'x': x, 'y': y})
      self.response.out.write(content)

template引擎語法

template語法主要區分為三大類:
  • 變數:在範本檔案中使用" {{ "及" }} "所包起來的部份就是直接要輸出的變數名稱。另外,template語法也提供一些用來運算變數資料的過濾器filter,例如" {{strName|lower}} ",就是在輸出變數strName時,使用lower過濾器調整資料後再進行輸出。
  • 標籤Tag:標籤的語法是以" {% "及 " %} "包起來的部份,主要是在做一些控制判斷的輸出動作,例如 " {% for i in list %} .....  {% endfor %}來執行一個迴圈。
  • 註解:用 " {# " 與 " #} "包起來的部份,template引擎會將它當作是程式的註解部份不去執行它所包含的部份。

變數輸出語法

在template引擎中,除了可以直接輸出變數內容外,它也支援複雜的資料結構變數,如dict的資料型態。

py程式:

class TestPage(webapp2.RequestHandler):
    def get(self):
    person={'name':'eric','email':'eric@gmail.com'}
    self.response.out.write(template.render('templates/test.html',{'p':person}))

template程式:

<html>
<head>

</head>
  <body>
    name : {{p.name}}<br />
    email: {{p.email}}<br />
  </body>
</html>

在原本的python程式中,我們要用person['name']及person['email']的方式才能取出dict中的資料,現在在template引擎語法中,我們只要用 " . "運算子即可。

除了dict的資料結構外," . "運算子還可以輸出物件的屬性及方法的回傳結果,如:

python程式:
class Person(object):
    def __init__(self,name,email):
    self.name=name
    self.email=email

    def getNickName(self):
    return 'good guy'

class TestPage(webapp2.RequestHandler):
    def get(self):
    person=Person('eric','eric@gmail.com')
    self.response.out.write(template.render('templates/test.html',{'p':person}))

template程式:
<html>
<head>
</head>
  <body>
    name : {{p.name}}<br />
    email: {{p.email}}<br />
    nick name: {{p.getNickName}}<br />
  </body>
</html>


" . "運算子也可以是list資料結構的index運算
python程式:
class TestPage(webapp2.RequestHandler):
    def get(self):
    person=['eric','eric@gmail.com']
    self.response.out.write(template.render('templates/test.html',{'p':person}))

template程式:
<html>
<head>

</head>
  <body>
    name : {{p.0}}<br />
    email: {{p.1}}<br />

  </body>
</html>

" . "運算子的判斷順序為:

  1. dict資料結構的key。
  2. 物件的屬性。
  3. 物件的方法。
  4. list資料結構的index位置。

變數輸出的過濾器

在template引擎變數輸出的語法中,可以使用 " | " 運算子加在變數後面來調整變數輸出的樣式,部份過濾器可以帶參數來運算。以下列出幾個範例:

  • add  將變數值加上一個設定的數值,如:{{num|add:1000}},最後加總的結果會以整數的資料型態出現。
  • date 將變數的日期進行格式化,如{{now|date:"Y-m-d H:i:s"}}
相關資料可參考

標籤語法

請參考





Google App Engine DB Datestore簡介

*將Python Tutorial專案分解成Django樣式範例程式

Google App Engine 提供一套易於延展的資料存放區,它是以Big-Table為基礎,與目前關聯式資料庫不同,在儲存設備上,資料物件稱為"資料實體"(entity),資料物件中有欄位資料,稱為該實體中的屬性(property),每一個資料實體的屬性,可以根據資料存放區所提供的資料型態來定義資料欄位。

資料欄位型態的定義可參考 https://developers.google.com/appengine/docs/python/datastore/entities

在程式中要表式資料實體的類別,可以用"模型"(model)程式來表示。

在程式碼中,所建立用來產生資料實體的類別必須繼承自db.Model母類別,資料實體可利用類別的方法put()來儲存資料。

from google.appengine.ext import db

class Account(db.Model):
    name=db.StringProperty(required=True)
    email=db.EmailProperty(required=True)
    created=db.DateTimeProperty(auto_now_add=True)

account=Account(name='Eric',email='litong@must.edu.tw')
account.put()

print account.created



Model欄位的資料型能定義


每一個類別中的資料欄位型態必須正確的定義,才能將資料正確的儲存。以下介紹幾種資料欄位的型態。


  • StringProperty

儲存文字資料,可單行或多行文字,資料大小不可超過500bytes。其選項multiline可為True或False,用來設定是否可儲存多行文字,若設定為False,則儲存的資料就不能有換行字元\r或\n,預設為False

from google.appengine.ext import db

class StringSample(db.Model):
    title=db.StringProperty(required=True)
    content=db.StringProperty(multiline=True,default='')
    created=db.DateTimeProperty(auto_now_add=True)

stringSample=StringSample(title='string property sample',content='''
Hi this is a String Property
Sample''')
stringSample.put()

print stringSample.content
print stringSample.created

  • ByteStringProperty

是用來儲存500byte大小以內的資料,可以是純文字或二進位資料,與StringProperty不同的地方在於,在ByteStringProperty欄位內的資料是尚未使用文字編碼過的byte資料。

from google.appengine.ext import db

class ByteStringSample(db.Model):
    content=db.ByteStringProperty(required=True)

byteStringSample=ByteStringSample(content='sometext')
byteStringSample.put()

print byteStringSample.content

  • TextProperty

是用來儲存較長的文字資料,但它無法製作索引、排序與條件比對。

from google.appengine.ext import db

class TextSample(db.Model):
    content=db.TextProperty()

textSample=TextSample(content=u'sometext')
textSample.put()

print textSample.content


  • BooleanProperty

儲存True或是False的資料型態
import datetime
from google.appengine.ext import db


class BooleanSample(db.Model):
    enabled=db.BooleanProperty(required=True,default=False)

booleanSample=BooleanSample()
booleanSample.put()

print booleanSample.enabled
booleanSample.enabled=True
booleanSample.put()
print booleanSample.enabled


  • IntegerProperty


儲存整數型態的資料

import datetime
from google.appengine.ext import db


class Sample(db.Model):
    value=db.IntegerProperty(required=True,default=0)

sample=Sample(value=123)
sample.put()
print sample.value



  • FloatProperty


儲存浮點數資料型態

import datetime
from google.appengine.ext import db


class Sample(db.Model):
    value=db.FloatProperty(required=True,default=0.0)

sample=Sample(value=123.456)
sample.put()
print sample.value


  • DateTimeProperty、DateProperty、TimeProperty


用來儲存日期及時間資料型態的資料,需要使用python中的datetime.datetime、datetime.date或是datetime.time物件。
其選項

  1. auto_now_add:可為True或False,來設定物件實體產生時,是否會自動寫入目前的日期或時間。
  2. auto_now:可為True或False,來設定物件實體更新時,是否會自動寫入目前的日期或時間。

import datetime
from google.appengine.ext import db
import datetime


class Sample(db.Model):
    setDay=db.DateProperty()
    created=db.DateTimeProperty(auto_now_add=True)
    updated=db.DateTimeProperty(auto_now=True)

sample=Sample()
sample.setDay=datetime.date(2014,7,5)
sample.put()
print sample.key(),sample.setDay,sample.created,sample.updated

getSample=db.get(sample.key())
getSample.setDay=datetime.date(2009,1,1)
getSample.put()
print getSample.key(),getSample.setDay,getSample.created,getSample.updated


  • ListProperty、StringListProperty


可以用來儲存list的資料型態,在ListProperty 中可放入要存放list內元素的資料型態,StringListProperty則無此選項。

import datetime
from google.appengine.ext import db
import datetime


class Sample(db.Model):
    order=db.ListProperty(int)
    tags=db.StringListProperty()

sample=Sample()
sample.order=[1,2,4,5,7,2]
sample.tags=[u'item',u'bar',u'data']
sample.put()
print sample.order,sample.tags


  • ReferenceProperty、SelfReferenceProperty


用來指向其它資料實體的資料欄位,儲存資料的資料型態為db.Key物件,可以使用這個欄位來作出各實體物件間的關聯。其選項:

  1. reference_class:為參考資料實體的資料模型
  2. collection_name:在參考資料模型中建立的list資料欄位名稱,若不指定則會使用"模型名稱_set"來作為資料欄位名稱。

import datetime
from google.appengine.ext import db

class User(db.Model):
    name=db.StringProperty()


class Article(db.Model):
    author=db.ReferenceProperty(reference_class=User)
    title=db.StringProperty()
    content=db.TextProperty()

u=User(name='Eric')
u.put()

article1=Article(author=u,title='title1',content='content1')
article1.put()

article2=Article(author=u,title='title2',content='content2')
article2.put()

article3=Article(author=u,title='title3',content='content3')
article3.put()

print article1.author.name



user=db.get('ahJkZXZ-Z2Fld2VhdGhlcnJpc2tyEQsSBFVzZXIYgICAgIDkmQkM')
print user.name

for article in u.article_set:
    print 'Article: %s'%article.title

但若有兩個資料欄位都要參考到同一個資料模型時,就會發生錯誤。

class Article(db.Model):
    author=db.ReferenceProperty(reference_class=User)
    reviewer=db.ReferenceProperty(reference_class=User)

就必須要設定不同的collection_name

import datetime
from google.appengine.ext import db

class User(db.Model):
    name=db.StringProperty()


class Article(db.Model):
    author=db.ReferenceProperty(reference_class=User,collection_name='writings')
    reviewer=db.ReferenceProperty(reference_class=User,collection_name='reviews')
    title=db.StringProperty()
    content=db.TextProperty()

'''
u1=User(name='Eric1')
u1.put()
u2=User(name='Eric2')
u2.put()

article1=Article(author=u1,reviewer=u1,title='title1',content='content1')
article1.put()

article2=Article(author=u2,reviewer=u1,title='title2',content='content2')
article2.put()

article3=Article(author=u1,reviewer=u2,title='title3',content='content3')
article3.put()

article4=Article(author=u2,reviewer=u2,title='title4',content='content4')
article4.put()

'''

u=db.get('ahJkZXZ-Z2Fld2VhdGhlcnJpc2tyEQsSBFVzZXIYgICAgIDUkQgM')
print dir(u.writings.order)

for article in u.writings.order('title'):
    print article.title


========================================================

如果要參考到相同的資料模型,可以使用SelfReferenceProperty,並用collection_name來設定欄位名稱。


class User(db.Model):
    name=db.StringProperty()
    employer=db.SelfReferenceProperty(collection_name='employees')



當資料儲存之後,GAE會配給每一個資料實體一個Key值(是經過加密的),這個值內容包含資料種類及ID,另外您也可以自己命名一個獨一無二的鍵值名稱key_name。ID值則是系統自動配發的數字。

由上述的例子我們可以得知,資料的儲存可以利用put( )的方法,資料的取得可以利用db.get( )的方法,其中必須加入資料實體的key值:

#u_key 為某個資料實體的key值
db.get(u_key)

同樣的,資料實體的刪除,我們可以利用db.delete( )的方法

#u_key 為某個資料實體的key值
db.delete(u_key)



資料的操作

  • 資料實體的建立

資料實體entity的建立作法,是必須要先定義資料模型的類別,然後再去產生資料物件。產生資料物件的方法有二:

  • 直接在物件產生的同時就定義欄位值

import datetime
from google.appengine.ext import db


class Sample(db.Model):
    value=db.IntegerProperty(default=0)

sample=Sample(value=123)
sample.put()
print sample.value



  • 先產生物件再指定欄位值


import datetime
from google.appengine.ext import db


class Sample(db.Model):
    value=db.IntegerProperty(default=0)

sample=Sample()
sample.value=456
sample.put()
print sample.value


資料的儲存可以利用物件類別put( )的方法或是透過db.put( )的方法

import datetime
from google.appengine.ext import db


class Sample(db.Model):
    value=db.IntegerProperty(default=0)

sample=Sample()
sample.value=777
db.put(sample)
print sample.value

透過Query物件來取得資料實體

我們可以透過資料模型的all( )方法取得一個Query物件,然後利用fetch(limit,offset)的方法來取得我們所要的資料。

from google.appengine.ext import db

class User(db.Model):
    name=db.StringProperty()


class Article(db.Model):
    author=db.ReferenceProperty(reference_class=User)
    title=db.StringProperty()
    content=db.TextProperty()

query=Article.all()
articles=query.fetch(2,0)

for article in articles:
    print article.title


我們可以利用filter( )方法來比對我們想要的資料

query=Article.all()
query.filter('title =','title1')
articles=query.fetch(2,0)

for article in articles:
    print article.title

==============

query=Article.all()
query.filter('title >','title1')
articles=query.fetch(2,0)

for article in articles:
    print article.title


我們可以利用order( )方法來排序資料

query=Article.all()
query.order('title')
articles=query.fetch(3,0)

for article in articles:
    print article.title

或是order內的參數加減號(- )取得相反順序的排序資料。

query=Article.all()
query.order('-title')
articles=query.fetch(3,0)

for article in articles:
    print article.title


GQL查詢語言

除了利用資料模型的all( )方法產生Query物件來讀取資料外,我們也可以使用一組資料查詢語言GQL來讀取資料。GQL是一個與SQL相似的查詢語言,目前GQL只提供資料的查詢讀取,不支援資料新增、刪除與修改。
from google.appengine.ext import db

class User(db.Model):
    name=db.StringProperty()


class Article(db.Model):
    author=db.ReferenceProperty(reference_class=User)
    title=db.StringProperty()
    content=db.TextProperty()

query=db.GqlQuery("SELECT * FROM Article WHERE title = 'title1'")
articles=query.fetch(10)
print articles

for article in articles:
    print article.title,article.content

================================================
可以使用參數編號或指定名稱來進行條件比對


query=db.GqlQuery("SELECT * FROM Article WHERE title = :1",'title1')

or

query=db.GqlQuery("SELECT * FROM Article WHERE title = :title",title='title1')

=======================================================
也可以透過資料模型來操作GQL,在方法中就不必再加入資料種類。

query=Article.gql("WHERE title = 'title1'")

or

query=Article.gql("WHERE title = :1",'title1')

or

query=Article.gql("WHERE title = :title",title='title1')


在GQL中可以指定LIMIT及OFFSET關鍵字,回傳的資料就直接是list了,而不是Query物件,因此就可以不必再呼叫fetch方法了

articles=Article.gql("WHERE title = :1 LIMIT 10 OFFSET 0",'title1')


另外,你也可以直接讀取資料的key值,以節省查詢的時間

keys=db.Query(Article,keys_only=True)

print keys
for key in keys:
    print key



keys=db.GqlQuery('SELECT __key__ FROM Article')



鍵值名稱

一般來說鍵值是經過加密過後所產生的一串無義意的編碼字串,即便內部包含資料種類的相關資訊,我們也無法從鍵值直接看出來,我們可以利用鍵值名稱key_name來自行命名每一個資料實體名稱,之後透過get_by_key_name( )方法來將資料取出。

import datetime
from google.appengine.ext import db


class Sample(db.Model):
    value=db.FloatProperty(required=True,default=0.0)

sample=Sample(key_name='555.5_sample',value=555.5)
sample.put()

sample=Sample.get_by_key_name('555.5_sample')
print sample.value

鍵值名稱與鍵值相同,必須是唯一的字串,使用鍵值名稱的好處是命名規則可以自訂,方便程式讀取資料實體。

刪除資料

要刪除資料可以用db.delete( )或物件實體delete( )方法來將資料刪除

db.delete(sample)

or

sample.delete()

您也可以使用db.delete( )的方法來刪除多筆資料(list)

import datetime
from google.appengine.ext import db


class Sample(db.Model):
    value=db.FloatProperty(required=True,default=0.0)

'''
sample1=Sample(key_name='555.5_sample',value=555.5)
sample1.put()

sample2=Sample(key_name='666.6_sample',value=666.6)
sample2.put()

print 'befere delete'
query=Sample.all()
query.filter('value >',555.0)
samples=query.fetch(10)

for sample in samples:
    print sample.value

'''
db.delete(samples)

print 'after delete'
query=Sample.all()
query.filter('value >',555.0)
samples=query.fetch(10)
for sample in samples:
    print sample.value


一對一(多)與多對多資料關聯

  • 一對一資料關聯

當你在設計郵件或電話通訊錄時,每一個使用者user可能會有一個以上的郵件或電話,這時你可以使用一對一關聯性將這些資料收集起來。

from google.appengine.ext import db

class User(db.Model):
    strName=db.StringProperty()

class Email(db.Model):
    user=db.ReferenceProperty(User,collection_name='emails')
    strType=db.StringProperty(required=True)
    emailAddr=db.EmailProperty(required=True)

u=User(strName='eric',key_name='eric_weng')
u.put()

office_mail=Email(user=u,strType='Office',emailAddr=db.Email('eric1@office.com'))
office_mail.put()
personal_mail=Email(user=u,strType='Personal',emailAddr=db.Email('eric2@personal.com'))
personal_mail.put()

在Email的資料模型中,有一個ReferenceProperty資料型態user指向類別User資料模型,並且在有一個emailAddr欄位來記錄email相關資料,如此就這兩個資料模型就具有一對多的關聯性。




我們可以透過user資料實體來取得與它相關的email資料實體,如下所示:

u=User.get_by_key_name('eric_weng')
for email in u.emails:
    print 'strType:%s,    emailAddr:%s'%(email.strType,email.emailAddr)

另外我們也可以從email資料實體來取得與它相關的user資料實體

u=User.get_by_key_name('eric_weng')
for email in u.emails:
     print 'strType:%s,    emailAddr:%s,    owner:%s'%(email.strType,email.emailAddr,email.user.strName)


  • 多對多關聯

當你在設計文章系統時,可能會有一篇文章屬於多個分類及一個分類有多篇文章之類的情形,此時你就需要將不同的資料模型建立多對多關聯。

from google.appengine.ext import db

class Article(db.Model):
    strTitle=db.StringProperty()
    strContent=db.TextProperty()
    lstCategories=db.ListProperty(db.Key)
    created=db.DateTimeProperty(auto_now_add=True)

class Category(db.Model):
    strName=db.StringProperty()
    strDescription=db.StringProperty(multiline=True)
 
    @property
    def getArticles(self):
        return Article.gql('WHERE lstCategories = :1',self.key())

上述例子中,Article資料模型使用了ListProperty的資料型態來記錄文章分類Category的key值,我們可以透過list取得每一個Category資料實體。
在Category的資料模型中,我們則建立一個getArticles的屬性,它可以根據Category資料實體的key值查詢到含有該鍵值的所有Article資料實體。
由此可知,對ListProperty欄位做" = "的運算,可以查詢到該資料是否存在list之中。

category1=Category(strName='Science',strDescription='About Science')
category1.put()
category2=Category(strName='Information',strDescription='About Information')
category2.put()

article1=Article(strTitle='Article1',strContent=db.Text('Article1 Content'))
article1.lstCategories=[category1.key(),category2.key()]
article1.put()

article2=Article(strTitle='Article2',strContent=db.Text('Article2 Content'))
article2.lstCategories=[category1.key()]
article2.put()

article3=Article(strTitle='Article3',strContent=db.Text('Article3 Content'))
article3.lstCategories=[category2.key()]
article1.put()


我們可以透過Article資料實體來取得它的所有分類。
for key in article1.lstCategories:
    print 'Category: %s'% db.get(key).strName

也可以透過某一個分類來取得所有該分類的所有文章,如此一來多對多的關聯查詢就容易許多了。
for article in category1.getArticles:
    print '[%s] %s'%(category1.strName,article.strTitle)


Expando 類別

當程式開發者希望使用一些比較彈性、鬆散的方式來定義資料型態時,可以使用Expando類別,它一樣繼承自Model類別,但它比Model 類別更具彈性,使用Expando所產生的資料物件,可以任意加上所要儲存的資料欄位,而且資料型態可以不固定,因此我們可以寫出下列程式

import datetime

from google.appengine.ext import db

class Song(db.Expando):
    title = db.StringProperty()

crazy = Song(title='Crazy like a diamond',author='Lucy Sky',publish_date='yesterday',rating=5.0)
crazy.put()

hoboken = Song(title='The man from Hoboken',author=['Anthony', 'Lou'],publish_date=datetime.datetime(1977, 5, 3))
hoboken.put()

crazy.last_minute_note=db.Text('Get a train to the station.')
crazy.put()

c=db.GqlQuery('SELECT * FROM Song WHERE publish_date = :1','yesterday').get()
print c.title


要刪除某一個欄位的資料,可以利用del關鍵字

c=db.GqlQuery('SELECT * FROM Song WHERE publish_date = :1','yesterday').get()
print dir(c)
print c.last_minute_note

del c.last_minute_note
c.put()

print c.last_minute_note


PolyModel類別

假設我們要製作一個通訊錄的資料模型,其中每一個聯絡人可能是個人或公司,雖然看起來是兩個不同的類別,但在通訊錄中可能會有相同欄位的存在,如電話號碼、地址等。在物件導向程式設計中,我們可能會利用"多型"(polymorphism)的概念來設計這樣的關係,如建立一個Contact類別,然後再建立Person及Company類別,再分別繼承自Contact類別。

from google.appengine.ext import db
from google.appengine.ext.db import polymodel

class Contact(polymodel.PolyModel):
    phone_number = db.PhoneNumberProperty()
    address = db.PostalAddressProperty()

class Person(Contact):
    first_name = db.StringProperty()
    last_name = db.StringProperty()
    mobile_number = db.PhoneNumberProperty()

class Company(Contact):
    name = db.StringProperty()
    fax_number = db.PhoneNumberProperty()

p = Person(phone_number='1-206-555-9234',
           address='123 First Ave., Seattle, WA, 98101',
           first_name='Alfred',
           last_name='Smith',
           mobile_number='1-206-555-0117')
p.put()

c = Company(phone_number='1-503-555-9123',
            address='P.O. Box 98765, Salem, OR, 97301',
            name='Data Solutions, LLC',
            fax_number='1-503-555-6622')
c.put()

for contact in Contact.all():
    print '%s : %s'%(contact.phone_number,contact.address)

for person in Person.all():
    print '%s %s: %s'%(person.first_name,person.last_name,person.phone_number)

在上述範例中,我們可以直接利用Contact.all( )來Query出繼承自它的所有Person與Company資料模型所產生的物件。

若想要只取出某個資料模型的物件,也可以使用各自的all( )方法。






















python 套件處理

python 套件處理

在python中,你可以將多個同類型或屬性的模組集中在同一個目錄中,並在目錄中加入 __init__.py的檔案,來組合成一個套件。

假設有個套件的目錄成員結構如下:
mylib/
       __init__.py
       myMathFun.py
       countNum.py


若要引入myMathFun模組,可以在檔案上方加入
from mylib import myMathFun

若要使用myMathFun中的類別物件或函式,可用
#main.py
from mylib import myMathFun
.....
.....
myMathFun.sumHierarchy(5)


在 __init__.py中所定義的物件類別或函式,可直接引入使用。
#mylib/__init__,py

def sayHello():
    print "hello')
   
#main.py
from mylib import sayHello
.....
.....
sayHello()
       

python 的模組設計

python 的模組設計

在應用程式開發的過程中,我們會將相關的函式或類別程式碼組合在同一支程式,這個我們稱之為模組,當這個模組需要被其它程式引用時,可以使用import 的指令來將模組引入,經常一個應用程式是必須要引用多個模組module才能完成應執行的任務。

對每一個python檔案而言,我們也可以將它視為一個模組,例如,我們在myMathFun.py檔案中設計一個可以計算階層加總的函式:

# myMathFun.py

def sumHierarchy(n):
    if n<2: return 1
    else: return n+sumHierarchy(n-1)



如果要在其它的程式中執行,就必須利用import 的語法或from xxx import xxx的語法將模組或模組中的函式引入:

# main.py
import myMathFun

def main():
    print '1+2+...5= %d'%myMathFun.sumHierarchy(5)

if __name__=='__main__':
    main()


當要引入特定物件類別或函式時:

# main.py
from myMathFun import sumHierarchy

def main():
    print '1+2+...5= %d'%sumHierarchy(5)

if __name__=='__main__':
    main()

執行結果為:

C:\Users\Administrator>python main.py
1+2+...5= 15


在上述例子中 __name__ 常數是每一個模組都有的內建常數,這個常數的值就是模組的名稱(也是檔案的名稱),當python直譯器在執行一個python檔案時,它會將此常數__name__的值改成__main__也就是切入程式執行的點,所以在此 if 判斷式中所執行的函式或程式就會在程式一開始執行時被載入運作。

python 自訂例外處理

python 自訂例外處理

在程式碼中,若能預測會發生的例外情形,你就可以使用raise關鍵字來手動抛出一個例外物件。

def getDivisionResult(intNum1,intNum2):
    if not intNum2:
        raise ZeroDivisionError('Number 2 can not be zero')
    else:
        return intNum1/intNum2

try:
    getDivisionResult(10,0)
except ZeroDivisionError as err:
    print 'Error Message is %s'%err

...
Error Message is Number 2 can not be zero
>>>


try:
    getDivisionResult(10,5)
except ZeroDivisionError as err:
    print 'Error Message is %s'%err
...
2
>>>

各種型式的error 物件,可參考:https://docs.python.org/2/library/exceptions.html

另外,我們也可以自行定義例外物件,自行定義的物件,必須要繼承自Exception物件類別。
class DivisionErr(Exception):
    def __init__(self,msg):
        self.message=msg

def getDivisionResult(intNum1,intNum2):
    if not intNum2:
        raise DivisionErr('Number 2 can not be zero')
    else:
        return intNum1/intNum2

try:
    getDivisionResult(10,0)
except DivisionErr as err:
    print err.message


...
Number 2 can not be zero

若你想要讓你定義的物件實體像一般常見到的例外物件一樣,能夠直接透過物件實體來輸出時,你必須在物件中加入 __str__ 的物件方法。

class DivisionErr(Exception):
    def __init__(self,msg):
        self.message=msg
   
    def __str__(self):
        return self.message

def getDivisionResult(intNum1,intNum2):
    if not intNum2:
        raise DivisionErr('Number 2 can not be zero')
    else:
        return intNum1/intNum2

try:
    getDivisionResult(10,0)
except DivisionErr as err:
    print 'Error message is %s'%err

...
Error message is Number 2 can not be zero


python的例外處理

python的例外處理

當python的程式碼在執行時,若發生語法或程式運算錯誤時,系統就會丟出一個例外(Exception)錯誤,如:

>>> i=0
>>> 10/i
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

>>> whild i<10: print i
  File "<stdin>", line 1
    whild i<10: print i
          ^
SyntaxError: invalid syntax
>>>

python 的物件繼承

python 的物件繼承

物件的繼承關係( inheritance),可簡化繁複設計的程式碼,讓物件保留母物件的屬性及方法,並可以加上或修改自己的屬性與方法,這種方式大大提高程式設計的效率,繼承的方法就是在物件類別名稱後的小括號中,放入要繼承的物件類別名稱(如Student物件類別繼承了object物件。

class Student(object):
    number=''
    def __init__(self,number):
        self.number=number
        self.__initSomething()
    def set_number(self,number):
        self.number=number
    def get_number(self):
        return self.number
    def __initSomething(self):
        print 'init something....'

class TimeWorker(Student):
    __name=''
    def __init__(self,number,name):
        self.number=number
        self.__name=name
    def set_name(self,name):
        self.__name=name
    def get_name(self):
        return self.__name

>>> tw=TimeWorker('N95050005','eric')
>>> tw.get_number()
'N95050005'
>>> tw.get_name()
'eric'

python 的物件設計

python 的物件設計

在python 中除了可以定義函式外,它也同樣有物件導向的設計,class關鍵字則是用來定義物件的類別,物件類別可以用來產生物件實體 ( instance) ,所以它也可以視為是一種資料型態。

class Student(object):
    number=''
    def __init__(self,number):
        self.number=number
    def set_number(self,number):
        self.number=number
    def get_number(self):
        return self.number

>>> std=Student('')
>>> print std.get_number()

>>> std=Student('N90000001')
>>> print std.get_number()
N90000001

>>> std.set_number('N00010001')
>>> print std.get_number()
N00010001
>>>

python 的流程控制

python 的流程控制

python 在執行程式時,都是依照程式碼的順序由上而下執行,若遇到程式需要做判斷、或需要反覆執行某幾行程式碼時,就需要改變程式執行的流程。

  • if 條件判斷式

當需要做條件判斷時,就必須使用 if 的條件判斷,如:

 x=30
 if x>30:
    print 'x is more than 30.'
 elif x==30:
 print 'x is equal to 30.'
 else:
     print 'x is small than 30.'
...
x is equal to 30.

if 的條件判斷可搭配 elif 及 else 來判斷其它的條件。每一個符合條件的區塊以 ":" 開始,可以用縮排來區分,但同一區段的縮排方式要一致。


python 函式處理

python 函式處理

當有一段程式碼需要重覆執行時,我們可以將此段程式碼定義成一個函式(function),以利作為後續呼叫使用。

  • 函式的定義

我們可以透過def 的方式來定義函式名稱
def <name>(arg1,arg2,.....,argN):
    <statements>

若有回傳值,可定義為
def <name>(arg1,arg2,.....,argN):
    <statements>
    return <vaule1,value2,.....,valueN)

如:
def plusAndMulti(x,y):
     intPlus=x+y
     intMulti=x*y
     print 'x+y=%s'%intPlus
     print 'x*y=%s'%intMulti

>>> plusAndMulti(10,20)
x+y=30
x*y=200
>>>

def getPlusResult(x,y):
     return x+y

>>> getPlusResult(10,20)
30


 def getFirstAndLastElement(lst):
     if not len(lst)>0: return None,None
     return lst[0],lst[len(lst)-1]

>>> lst=[1,2,3,4,5]
>>> getFirstAndLastElement(lst)
(1, 5)

>>> lst=[5]
>>> getFirstAndLastElement(lst)
(5, 5)

>>> lst=[]
>>> getFirstAndLastElement(lst)
(None, None)

>>> lst=[1,2,3,4,5]
>>> b,c=getFirstAndLastElement(lst)
>>> print b,c
1 5

python 物件型態取得使用方法協助-help 指令



python  物件型態取得使用方法協助-help 指令


當你對python物件使用上有不清楚或想知道更多使用方法時,可利用dir或help的指令直接對已指定值的變數或方法套上這兩個指令即可。


dir函式只是給出可使用的方法或屬性名稱,要詳細了解這個方法或名稱如何使用,就必須用help的函式了。




>>> S
'Hello'
>>> dir(S)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__
ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__g
t__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__
', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '
__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count', 'decode',
'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdi
git', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lst
rip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit'
, 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', '
translate', 'upper', 'zfill']


python 物件型態取得使用方法協助 dir

python  物件型態取得使用方法協助-dir 指令


當你對python物件使用上有不清楚或想知道更多使用方法時,可利用dir或help的指令直接對已指定值的變數或方法套上這兩個指令即可。


當你呼叫內建的dir函式時,會傳回來指定物件可用的屬性及方法串列。



>>> S='hello'
>>> dir(S)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__
ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__g
t__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__
', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '
__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count', 'decode',
'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdi
git', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lst
rip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit'
, 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', '
translate', 'upper', 'zfill']
>>>

GAE-Datastore中文資料儲存問題

GAE-Datastore中文資料儲存問題

1. 中文資料可以unicode的方式存進datastore
ex:


from google.appengine.ext import db
from google.appengine.ext.db import polymodel
import urllib,sys


class Contact(polymodel.PolyModel):
    phone=db.PhoneNumberProperty()
    address=db.PostalAddressProperty()


class Person(Contact):
    first_name=db.StringProperty()
    last_name=db.StringProperty()
    mobile=db.PhoneNumberProperty()


class Company(Contact):
    title=db.StringProperty()
    fax=db.PhoneNumberProperty()


strAddr=unicode('這是中文','utf-8')
p=Person(first_name='eric',last_name='abc',mobile=db.PhoneNumber('0920891999'),phone=db.PhoneNumber('0912123456'),address=db.PostalAddress(strAddr))
p.put()


c=Company(title='Foo Company',phone=db.PhoneNumber('091234567'),address=db.PostalAddress(strAddr),fax=db.PhoneNumber('09123456789'))
c.put()