关于什么是Google App Engine(GAE)以及初始开发环境的配置。已经有许多的先驱者介绍了,我也就不再画蛇添足了。
限于本人表达能力有限,我就以贴代码为主。附带少数说明
第一步:建立数据实体。
此处我建了两个实体。一个用来存放导航分类
一个用来存放具体的分类信息
下面是代码


1 #导航分类
2 class NavType(db.Model):
3 typeName = db.StringProperty()
4 userEmail = db.StringProperty()
5 dataCount = db.IntegerProperty()
6 #具体导航信息
7 class UserNav(db.Model):
8 #登录者邮件地址
9 userEmail = db.StringProperty()
10 #登录的分类KEY
11 navTypeKey = db.StringProperty()
12 #导航描述
13 navDes = db.StringProperty(multiline=True)
14 #导航标题
15 navName = db.StringProperty()
16 #导航链接
17 navLink = db.StringProperty()
18 #附加字段 排序查询用 本来想用时间做查询排序的可时间类型我不知道怎么转换就放弃了
19 navNumIndex = db.IntegerProperty()
20 #登录时间
21 navDate = db.DateTimeProperty(auto_now_add=True)
第二步:添加,修改数据。


1 navInfo = {}
2 typeInfo = {}
3 #这个值主要是分页时用
4 navNumIndex = long(time.time())
5 #前台页面传的值,新添加时为空
6 navKey = self.request.get('navKey')
7
8 navName = self.request.get('navName')
9 navLink = self.request.get('navLink')
10 navDes = self.request.get('navDes')
11 navTypeName = self.request.get('navTypeName')
12 navTypeKey = self.request.get('typeKey')
13
14 #--------------取得导航实体------------------------------------------------------------------
15 if navKey:
16 navInfo = db.get(db.Key(navKey))
17 else:
18 navInfo = DB.UserNav()
19
20 #--------------取得分类key----------------------------------
21 #这里标识一下是否需要新建一个分类
22 newTypeFlg = True
23 if navTypeKey:
24 typeInfo = db.get(db.Key(navTypeKey))
25 navTypeCount = 0
26 if typeInfo.typeName != navTypeName:
27 navTypeCount = typeInfo.dataCount -1
28 if navTypeCount>0:
29 typeInfo.dataCount = navTypeCount
30 typeInfo.put()
31 else:
32 db.delete(typeInfo)
33 else:
34 newTypeFlg = False
35 #根据名检索一下是否已经存在修改后或新输入的分类名
36 typeResult = db.GqlQuery("SELECT * FROM NavType WHERE typeName=:1 AND userEmail=:2",navTypeName,userEmail).fetch(1)
37 if len(typeResult) == 1:
38 typeObj = typeResult[0]
39 #增加此分类的条数
40 typeObj.dataCount=typeObj.dataCount+1
41 typeObj.put()
42 navTypeKey = str(typeObj.key())
43 newTypeFlg = False
44
45 if newTypeFlg:
46 typeObj = DB.NavType()
47 typeObj.typeName = navTypeName
48 typeObj.userEmail = userEmail
49 #第一次添加时为1
50 typeObj.dataCount=1
51 typeObj.put()
52 navTypeKey = str(typeObj.key())
53
54 navInfo.navTypeKey=navTypeKey
55 navInfo.navName=navName
56 navInfo.navLink=navLink
57 navInfo.navDes=navDes
58 navInfo.userEmail=userEmail
59 navInfo.navNumIndex = navNumIndex
60 navInfo.put()
第三步:选择,显示数据
这里用了JQuery的Ajax方法因此返回的是构建好的json格式。对于特殊字符也进行了转换。先发特殊字符转换函数,以便于理解。


1 def strReplace(str):
2 #说明 |squot| 单引号;|dquot| 双引号; |colon| 冒号
3 #|lbrace| |rbrace| 左右大括号; |lbracket| |rbracket| 左右中括号; |comma|逗号;
4 # 以上为json语法符号 在最后需要原样转回
5 replaceMap = {"<":"<",">":">","{":"{","}":"}","[":"[","]":"]","(":"(",")":")",
6 "\"":""","'":"'",",":",",":":":",";":";","\r\n":" ","\n":" "}
7 returnMap = {"|squot|":"'","|dquot|":"\"","|colon|":":","|lbrace|":"{","|rbrace|":"}",
8 "|lbracket|":"[","|rbracket|":"]","|comma|":","}
9 if str:
10 #这两个for语句有先后顺序
11 for key,val in replaceMap.items():
12 str = str.replace(key,val)
13 for key1,val1 in returnMap.items():
14 str = str.replace(key1,val1)
15 else:
16 str = ""
17 return str
18 def getNavTypeJson(typeObj):
19 #这里键值写 text value 是为了在前台与dropdownlist的选项统一 容易遍历
20 jsonStr = "|squot|text|squot||colon||squot|"+typeObj.typeName + "|squot||comma||squot|value|squot||colon||squot|"+str(typeObj.key())+"|squot|"
21 jsonStr += "|comma||squot|dataCount|squot||colon||squot|"+str(typeObj.dataCount)+"|squot|"
22 return jsonStr
23 def getNavListJson(navObj,showModel):
24 jsonStr = ""
25 #返回关键字及形式 'navName':'name','navLink':'link','navType':'typeKey','navKey':'navKey','navNumIndex':'navNumIndex','navDes':'description'
26 jsonStr = "|squot|navName|squot||colon||squot|"+navObj.navName + "|squot||comma||squot|navLink|squot||colon||squot|"+navObj.navLink
27 jsonStr += "|squot||comma||squot|navNumIndex|squot||colon||squot|"+str(navObj.navNumIndex)+ "|squot||comma||squot|navKey|squot||colon||squot|"+str(navObj.key())+"|squot|"
28 if showModel=='full':
29 jsonStr += "|comma||squot|navDes|squot||colon||squot|"+navObj.navDes+"|squot||comma||squot|navTypeKey|squot||colon||squot|"+navObj.navTypeKey + "|squot|"
30
31 return jsonStr
下面是选取数据的代码


1 navList = []
2 navTypeList = []
3 results = []
4 typeJsonStr = '|lbracket||rbracket|'
5
6 #前一页 后一页按钮的可用状态
7 pageNext = False
8 pagePre = False
9
10 #默认完整模式 一页10条数据
11 pageSize = 10;
12
13
14 #以简单模式还是完整模式查看
15 showModel = self.request.get('showModel')
16 if showModel == 'sample':
17 pageSize = 100
18
19 #分页数据取得时的index值
20 pageKey = self.request.get('pageKey')
21 #取上一页还是下一个数据
22 pagingTo = self.request.get('pagingTo')
23 #选择条件 分类key
24 navTypeKey = self.request.get('typeKey')
25
26 #页当前返回数据所在页号
27 pageCountIndex = int(self.request.get('pageCountIndex'))
28
29 whereStr = "WHERE userEmail = :1 "
30 #当点击上一页或下一页按钮时
31 if pageKey:
32 if pagingTo == 'next':
33 if pageCountIndex>pageSize:
34 pageCountIndex = (pageCountIndex-1)/pageSize
35 pageCountIndex += 1
36 ##sys.__stdout__.write("wen"+str(pageCountIndex))
37 if navTypeKey:
38 whereStr = whereStr + " AND navNumIndex<:2 AND navTypeKey=:3 ORDER BY navNumIndex DESC"
39 results = DB.UserNav.gql(whereStr,userInfo["email"],long(pageKey),navTypeKey).fetch(pageSize+1)
40 else:
41 whereStr = whereStr + " AND navNumIndex<:2 ORDER BY navNumIndex DESC"
42 results = DB.UserNav.gql(whereStr,userInfo["email"],long(pageKey)).fetch(pageSize+1)
43 navCount = len(results)
44 #results的结果就是相对点击按钮时的下一页的数据
45 #数据条数大于0说明有下一页的数据则上一页按钮就可用了
46 if navCount > 0:
47 pagePre = True
48 #数据条数大于20说明有下下一页的数据则下一页按钮就可用了
49 if navCount > pageSize:
50 #删除最后一个元素
51 results.pop()
52 pageNext = True
53 elif pagingTo == 'pre':
54 if pageCountIndex>pageSize:
55 pageCountIndex = (pageCountIndex-1)/pageSize
56 pageCountIndex -= 1
57
58
59 #这里用了一个等于号就是为了判断是否有上一页的数据
60 if navTypeKey:
61 whereStr = whereStr + " AND navNumIndex>:2 AND navTypeKey=:3 ORDER BY navNumIndex ASC"
62 results = DB.UserNav.gql(whereStr,userInfo["email"],long(pageKey),navTypeKey).fetch(pageSize+1)
63 else:
64 whereStr = whereStr + " AND navNumIndex>:2 ORDER BY navNumIndex ASC"
65 results = DB.UserNav.gql(whereStr,userInfo["email"],long(pageKey)).fetch(pageSize+1)
66
67 navCount = len(results)
68 #results的结果就是相对也点击按钮时的前一页的数据
69 #数据条数大于0说明有上一页的数据则下一页按钮就可用了
70 if navCount > 0:
71 pageNext = True
72 #数据条数大于pageSize说明有上上一页的数据则上一页按钮就可用了
73 if navCount > pageSize:
74 #删除最后一个元素
75 results.pop()
76 pagePre = True
77 #此处是按升序查询的数据因此显示的时候需要在把数组反正一下
78 results.reverse()
79 #页面第一次运行或选择一个分类后
80 else:
81 #取某一个分类的所有信息
82 if navTypeKey:
83 results= DB.UserNav.gql(whereStr+" AND navTypeKey=:2 ORDER BY navNumIndex DESC",userInfo["email"],navTypeKey).fetch(pageSize+1)
84 else:
85 #第一次运行时取所有分类
86 typeResults = DB.NavType.gql("WHERE userEmail = :1 ",userInfo["email"]).fetch(1000)
87
88 for typeObj in typeResults:
89 navTypeList.append(getNavTypeJson(typeObj))
90 typeJsonStr = "|lbracket||lbrace|"+"|rbrace||comma||lbrace|".join(navTypeList)+"|rbrace||rbracket|"
91
92 results= DB.UserNav.gql(whereStr+" ORDER BY navNumIndex DESC",userInfo["email"]).fetch(pageSize+1)
93
94 #此处是页面第一次运行的时候,前一页按钮肯定不好用
95 if len(results) >pageSize:
96 pageNext = True
97 #删除最后一个元素
98 results.pop()
99 pageCountIndex = (pageCountIndex*pageSize) + 1
100 retJsonStr += "|comma||squot|pageCountIndex|squot||colon||squot|"+str(pageCountIndex)+"|squot|"
101 retJsonStr += "|comma||squot|navTypes|squot||colon|"+typeJsonStr+"|comma||squot|pagePre|squot||colon||squot|"+str(pagePre)+"|squot|"
102 retJsonStr += "|comma||squot|pageNext|squot||colon||squot|"+str(pageNext)+"|squot||comma||squot|navList|squot||colon||lbracket|"
103 if len(results) > 0:
104 for nav in results:
105 navList.append(getNavListJson(nav,showModel))
106 retJsonStr += "|lbrace|"+"|rbrace||comma||lbrace|".join(navList)+"|rbrace|"
107 retJsonStr += "|rbracket|"
108 self.response.out.write(settings.strReplace("|lbrace|"+retJsonStr+"|rbrace|"))
第四步 数据删除,分类修改
首先是数据删除部分


1
2 #要删除数据的KEY,删除全部时此值为空
3 navKey = self.request.get('navKey')
4 typeKey = self.request.get('typeKey')
5 if navKey:#删除一个
6 result = db.get(db.Key(navKey))
7 db.delete(result)
8
9 #取得所属分类
10 navType = db.get(db.Key(typeKey))
11 dataCount = navType.dataCount-1
12 if dataCount>0:
13 navType.dataCount = navType.dataCount-1;
14 navType.put()
15 else:
16 db.delete(navType)
17 else:#删除全部
18 #Google提供的查询最多返回1000条数据因此这里设定了一个循环删除数据
19 delNavFlg = True
20 while delNavFlg:
21 results = db.GqlQuery("SELECT * FROM UserNav WHERE userEmail = :1",userEmail).fetch(500)
22 if len(results) <1:
23 delNavFlg = False
24 db.delete(results)
25 #导航删除以后所有分类也删除
26 delTypeFlg = True
27 while delTypeFlg:
28 results = db.GqlQuery("SELECT * FROM NavType WHERE userEmail = :1",userEmail).fetch(500)
29 if len(results) <1:
30 delTypeFlg = False
31 db.delete(results)
分类修改部分


1 typeKey = self.request.get('typeKey')
2 typeName = self.request.get('typeName')
3 result = db.get(db.Key(typeKey))
4 if typeName:
5 result.typeName=typeName
6 result.put()
7 else:
8 db.delete(result)
9 #删除当前分类包含的所有导航信息
10 delFlg = True
11 while delFlg:
12 results = db.GqlQuery("SELECT * FROM UserNav WHERE userEmail = :1 AND navTypeKey=:2",userEmail,typeKey).fetch(500)
13 if len(results) <1:
14 delFlg = False
15 db.delete(results)
对于GAE我是也就是刚刚看到其门槛的菜鸟,写这些只是希望能对刚刚接触的人提供一点参考。
完整的代码由于还包括其他的小应用,我就不主动拿出来丢人了。有需要参考一下的可以单独联系。
有想看实例的可以到http://daily-manage.appspot.com/这里看。我已经上传到Google的服务器上去了。