# 如何在 GAE 上佈署 jQuery EasyUI 專案 (七) : EasyUI CMS on GAE 之 3
此文實作了 GAE 上完整的 CRUD 操作, 所以套用此模型應該可以快速搞定導覽列的部分. 以前實作 PHP 版 CMS 時, 我把導覽區塊跟導覽連結管理放在一個頁籤中顯示與管理, 雖然這兩個資料表有連動, 但總覺得有點彆扭. 在 GAE 版我想把它們拆開到兩個頁籤去分開管理.
首先在 model.py 中新增導覽區塊的資料表模型 Navblocks 如下 :
class Navblocks(db.Model):
name=db.StringProperty()
title=db.StringProperty()
sequence=db.IntegerProperty()
display=db.BooleanProperty()
class Navlinks(db.Model):
name=db.StringProperty()
title=db.StringProperty()
url=db.StringProperty()
target=db.StringProperty()
sequence=db.IntegerProperty()
block_name=db.StringProperty()
hint=db.StringProperty()
navblock=Navblocks(key_name="main",name="main",title=u"主功能",
sequence=0,display=True)
navblock.put()
navlink=Navlinks(key_name="home",name="home",title=u"首頁",
url="javascript:gohome()",target="_self",sequence=0,block_name="main",
hint=u"首頁")
navlink.put()
navlink=Navlinks(key_name="logout",name="logout",title=u"登出",
url="javascript:logout()",target="_self",sequence=0,block_name="main",
hint=u"登出")
navlink.put()
資料表 Navblocks 裡的 name 欄位用來與 key_name 同步作為主鍵, display 則用來控制導覽區塊是否要顯示, 預設先建立一個名為 main 的主功能導覽區塊. 而 Navlinks 資料表則是從上一篇文章的 Headerlinks 複製過來修改的, 主要是新增了 block_name 欄位來表示此連結屬於哪一個導覽區塊, 預設先建立 home 與 logout 兩個資料實體.
有了這兩張資料表後, 就可以在主要網頁版面中以程式控制左方導覽列, 首先在 main.py 中增加 main_5 的路徑處理類別 :
class main_5(BaseHandler):
def get(self):
#save visitor info
ip=self.request.remote_addr
user_agent=self.request.headers.get("User-Agent")
visitor=m.Visitors()
visitor.ip=ip
visitor.visit_time=datetime.datetime.now() + timedelta(hours=+8)
visitor.user_agent=user_agent
visitor.ip_list=list(ip)
visitor.user_agent_list=list(user_agent)
visitor.put()
#check login session
info={} #for storing parameters
account=self.session.get('account')
if account: #already logged in
info["account"]=account
s=m.Settings.get_by_key_name("settings")
info["site_title"]=s.site_title
theme=self.session.get('theme')
info["theme"]=theme
#create param: greeting
today=datetime.date.today()
week=[u"一",u"二",u"三",u"四",u"五",u"六",u"日"]
info["greeting"]=u"您好! " + account + u", 今天是 " + \
str(today.year) + u" 年 " + str(today.month) + u" 月 " + \
str(today.day) + u" 日 星期" + week[today.weekday()]
#create param: themes
theme_list=[]
themes=m.Themes.all()
for t in themes:
theme_list.append(t.theme)
info["themes"]=theme_list
#create param: headerlinks
headerlinks=m.Headerlinks.all()
headerlinks.order("-sequence") #sort by sequence (reverse)
link_list=[] #for storing headerlinks objects
for h in headerlinks:
link={}
link["title"]=h.title
link["url"]=h.url
link["target"]=h.target
link["sequence"]=h.sequence
link["hint"]=h.hint
link_list.append(link)
info["headerlinks"]=link_list
#create param: navblocks & navlinks
navblocks=m.Navblocks.all()
navblocks.filter("display =",True)
navblocks.order("sequence") #sort by sequence
navblock_list=[] #for storing navblocks objects
for nb in navblocks:
navblock={}
navblock["title"]=nb.title #store block title
#query nvavlinks belongs to this block
query=m.Navlinks.all()
navlinks=query.filter("block_name =",nb.name)
navlinks.order("sequence")
navlink_list=[] #for storing navblinks objects
for nl in navlinks:
navlink={}
navlink["title"]=nl.title
navlink["url"]=nl.url
navlink["target"]=nl.target
navlink["hint"]=nl.hint
navlink_list.append(navlink) #store this link
navblock["navlinks"]=navlink_list #store block links
navblock_list.append(navblock) #store this block
info["navblocks"]=navblock_list
url="templates/main_5.htm"
else: #not logged in
s=m.Settings.get_by_key_name("settings")
info["site_title"]=s.site_title
info["site_theme"]=s.site_theme
url="templates/login.htm"
path=os.path.join(os.path.dirname(__file__), url)
content=template.render(path,{'info':info})
self.response.out.write(content)
這是從 main_4 複製過來修改的, 藍色部分就是增加的部分, 先從 Navblocks 讀取 display 欄位為 True 的全部實體, 並建立一個 navblock_list 串列來儲存要傳遞給網頁模板的參數, 然後逐一拜訪每一個 Navblocks 實體, 在 for 迴圈中首先設立一個 navblock 字典物件來記錄這個區塊的 title 與所擁有的連結. 首先是將每一個瀏覽區塊的 title 屬性值紀錄在 navblock 字典的 title 屬性裡, 然後去查詢 Navlinks 資料表, 把 block_name 欄位值為目前區塊名稱的資料實體過濾出來, 並設立一個 navlink_list 串列來記錄屬於此區塊之所有連結. 接著以第二層 for 迴圈拜訪過濾後的查詢物件, 並設立一個 navlink 字典物件來記錄屬於這個區塊的所有連結. 每一個區塊的連結組成的串列會被記錄在 navblock 字典的 navlinks 屬性中; 而所有的區塊串列則記錄在 info 字典的 navblocks 屬性裡, 當作參數傳給此類別所渲染的 main_5.htm 網頁.
以預設的資料儲存設定來說, 上面這個程序所產生的 navblock_list 串列的結構如下 :
[{"title":"主功能",
"navlinks":[{"title":"首頁",
"url":"javascript:gohome()",
"target":"_self",
"hint":"首頁"},
{"title":"登出",
"url":"javascript:logout()",
"target":"_self",
"hint":"登出"}
]
}]
這個串列會被設為 info 字典的 navblocks 屬性傳遞給 main_5.htm, 此網頁模板是從 main_4.htm 複製修改而來, 內容如下 :
{% extends "jqueryeasyui.htm" %}
{% block style %}
a {text-decoration:none;}
a:hover {text-decoration:underline;background-color:yellow;}
#west {width:150px;}
#west-inner {border-top:0px;border-right:0px;border-bottom:0px;}
.nav {padding:5px;}
.tab {padding:10px;}
#north {height:55px;overflow:hidden;}
#north-table {width:100%;border-spacing:0px}
#north-left {text-align:left;padding:5px;}
#north-right {text-align:right;padding:5px;}
{% endblock%}
{% block body %}
<div id="north" title="{{ info.site_title }}" data-options="region:'north',border:false,collapsible:true,tools:'#tools'">
<form id="header-form" method="post">
<table id="north-table">
<tr>
<td id="north-left" style="vertical-align:middle">
{{ info.greeting }}
</td>
<td id="north-right" style="vertical-align:middle">
<span id="header_links">
{% for h in info.headerlinks %}
<a href="{{h.url}}" target="{{h.target}}" title="{{h.hint}}">{{h.title}}</a>.
{% endfor %}
</span>
<select id="theme_sel" name="theme" class="easyui-combobox" style="width:120px;height:18px" panelHeight="auto">
<option value="default">主題布景</option>
{% for t in info.themes %}
<option value="{{t}}"{% ifequal t info.theme %} selected{% endifequal %}>{{t}}</option>
{% endfor %}
</select>
</td>
</tr>
</table>
</form>
</div>
<div id="tools">
<a href="javascript:logout()" class="icon-remove" title="登出"></a>
</div>
<div title="導覽" data-options="region:'west',border:true" id="west">
<div class="easyui-accordion" id="west-inner">
{% for block in info.navblocks %}
<div title="{{ block.title }}" class="nav">
{% for link in block.navlinks %}
.<a href="{{ link.url }}" target="{{ link.target }}" title="{{ link.hint }}">{{ link.title }}</a><br>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
<div data-options="region:'center',border:false,href:'/systabs'" id="center">
</div>
<script>
$("body").attr("class", "easyui-layout");
$(document).ready(function(){
//var css="http://www.jeasyui.com/easyui/themes/{{ info.theme }}/easyui.css";
var css="/static/easyui/themes/{{ info.theme }}/easyui.css";
$("#theme").attr("href", css);
$("#theme_sel").combobox({
onSelect:function(rec){
//var css="http://www.jeasyui.com/easyui/themes/" + rec.value + "/easyui.css";
var css="/static/easyui/themes/" + rec.value + "/easyui.css";
$("#theme").attr("href", css);
$.get("/change_theme",{theme:rec.value});
}
});
});
<!-- 系統的載入與登出函式 -->
function gohome(){
$(function(){
var p=$("body").layout("panel","center");
p.panel({href:"/systabs"});
});
}
function logout(){
$(function(){
$.messager.confirm("確認","確定要登出系統嗎?",function(btn){
if (btn) {window.location.href="/logout";}
});
});
}
</script>
{% endblock%}
上面藍色部份就是修改的地方, 從 info 字典的 navblocks 屬性取得導覽區塊串列後以 for 迴圈來遍歷, 然後從每一個區塊的 navlinks 屬性取出此區塊所有連結之串列, 以此作為第二層迴圈來產生超連結, 這樣便完成了左方導覽區的顯示了.
接下來是導覽區塊的管理, 我先在 model.py 增加一個 Systabs 模型的頁籤實體 list_navblocks :
systab=Systabs(key_name="list_navblocks",tab_name="list_navblocks",
tab_label=u"導覽區塊",tab_link="/list_navblocks",tab_order=4,tab_admin=True)
systab.put()
此頁籤會從路徑 list_navblocks 載入資料, 然後在 main.py 中新增此路徑 list_navblocks 的處理類別 :
class list_navblocks(webapp2.RequestHandler):
def get(self):
url="templates/list_navblocks.htm"
path=os.path.join(os.path.dirname(__file__), url)
content=template.render(path,{})
self.response.out.write(content)
它所渲染的網頁 list_navblocks.htm 內容如下, 係參考上一篇文章中 list_headerlinks.htm 修改而來 :
{% extends "jqueryeasyui.htm" %}
{% block style %}
body {font: 80% "Trebuchet MS", sans-serif; margin: 50px;}
{% endblock%}
{% block body %}
<!--導覽區塊 Navblocks 列表-->
<table id="sys_nav_blocks" title="導覽區塊列表" style="width:auto" data-options="tools:'#nav_blocks_tools'"></table>
<div id="nav_blocks_tools">
<a href="#" id="add_nav_block" class="icon-add" title="新增"></a>
<a href="#" id="edit_nav_block" class="icon-edit" title="編輯"></a>
<a href="#" id="remove_nav_block" class="icon-remove" title="刪除"></a>
<a href="#" id="reload_nav_blocks" class="icon-reload" title="重新載入"></a>
</div>
<!--新增&編輯 Navblocks 表單對話框-->
<div id="nav_block_dialog" class="easyui-dialog" title="新增區塊" style="width:360px;height:220px;" data-options="closed:'true',buttons:'#nav_block_buttons'">
<form id="nav_block_form" method="post" style="padding:10px">
<div style="margin:5px">
<label style="width:60px;display:inline-block;">名稱 : </label>
<input name="name" id="block_name" type="text" class="easyui-textbox" data-options="missingMessage:'此欄位必須為英數字組合',required:true,readonly:false" style="width:230px">
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">標題 : </label>
<input name="title" type="text" class="easyui-textbox" data-options="missingMessage:'此欄位為必填',required:true" style="width:230px">
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">順序 : </label>
<input id="nav_block_sequence" data-options="missingMessage:'此欄位為必填',required:true" name="sequence">
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">顯示 : </label>
<select id="display" name="display" class="easyui-combobox" data-options="panelHeight:'auto'">
<option value="True">True</option>
<option value="False">False</option>
</select>
<input type="hidden" id="nav_block_op" value="">
</div>
</form>
</div>
<div id="nav_block_buttons" style="padding-right:15px;">
<a href="#" id="clear_nav_block" class="easyui-linkbutton" iconCls="icon-clear" style="width:90px">重設</a>
<a href="#" id="save_nav_block" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">確定</a>
</div>
<script>
$(function(){
//標頭連結 sys_nav_blocks
$('#sys_nav_blocks').datagrid({
columns:[[
{field:'name',title:'name',sortable:true},
{field:'title',title:'title',sortable:true},
{field:'sequence',title:'sequence',align:'center',sortable:true},
{field:'display',title:'display',sortable:true}
]],
url:"/get_navblocks",
method:"post",
singleSelect:true,
rownumbers:true
});
$("#nav_block_sequence").numberspinner({
min:0,
max:99,
increment:1,
value:"0",
required:true,
missingMessage:'此欄位為必填'
});
$("#clear_nav_block").bind("click",function(){
$("#nav_block_form")[0].reset();
});
$("#save_nav_block").bind("click",function(){
var op=$("#nav_block_op").val(); //判斷是新增或修改
if (op=="update") {
var row=$("#sys_nav_blocks").datagrid("getSelected");
if (row.name=="home" || row.name=="logout") {
$("#nav_block_dialog").dialog('close');
$.messager.alert("訊息","此為系統導覽區塊不可更改!","error");
return;
}
var url="/update_navblock";
}
else {var url="/add_navblock";}
$("#nav_block_form").form("submit",{
url:url,
method:"post",
success:function(data){
var data=eval('(' + data + ')');
$("#nav_block_dialog").dialog("close");
if (data.status==="success") {
$("#sys_nav_blocks").datagrid("reload");
}
else {
$.messager.alert("訊息",data.reason,"error");
}
}
});
});
$("#add_nav_block").bind("click",function(){
$("#nav_block_dialog").dialog("open").dialog("setTitle","新增區塊");
$("#block_name").textbox({"readonly":false}); //for adding
$("#nav_block_form").form("clear");
$("#nav_block_op").val("add");
$('#display').combobox('setValue','True');
});
$("#edit_nav_block").bind("click",function(){
var row=$("#sys_nav_blocks").datagrid("getSelected");
if (row) {
$("#nav_block_dialog").dialog("open").dialog("setTitle","編輯區塊");
$("#nav_block_form").form("load",row);
$("#block_name").textbox({"readonly":true});
$("#nav_block_op").val("update");
}
else {$.messager.alert("訊息","請先選取要編輯的區塊!","error");}
});
$("#remove_nav_block").bind("click",function(){
var row=$("#sys_nav_blocks").datagrid("getSelected");
if (row) {
var params={name:row.name};
var msg="確定要刪除這個區塊嗎?<br>這樣會刪除此區塊全部連結."
$.messager.confirm("確認",msg,function(btn){
if (btn){
if (row.name=="main") {
$.messager.alert("訊息","此為系統導覽區塊不可刪除!","error");
return;
}
var callback=function(data){
if (data.status==="success"){
$("#sys_nav_blocks").datagrid("reload");
}
else {$.messager.alert("訊息",data.reason,"error");}
};
$.post("/remove_navblock",params,callback,"json");
}
})
}
});
$("#reload_nav_blocks").bind("click",function(){
$("#sys_nav_blocks").datagrid("load");
});
});
</script>
{% endblock%}
這裡刪除區塊時, 彈出訊息加上了會同時刪除所屬連結的警語. CRUD 操作部分, 此網頁模板會從 /get_navblocks 路徑取得 json 資料來源, 其路徑處理類別如下, 是從上一篇文章的 /get_headerlinks 複製來修改的 :
class get_navblocks(webapp2.RequestHandler):
def post(self):
page=self.request.get("page")
rows=self.request.get("rows")
sort=self.request.get("sort")
order=self.request.get("order")
if len(page):
page=int(page)
else:
page=1
if len(rows):
rows=int(rows)
else:
rows=10
if not len(sort):
sort="sequence"
if not len(order):
order="asc"
query=m.Navblocks.gql("ORDER BY %s %s" % (sort, order))
count=query.count()
blocks=query.fetch(rows, (page-1)*rows)
rows=[] #for storing objects
for b in blocks:
block={"name":b.name,
"title":b.title,
"sequence":b.sequence,
"display":b.display}
rows.append(block)
obj={"total":count,"rows":rows} #Easyui datagrid json format
self.response.headers["Content-Type"]="application/json"
self.response.out.write(json.dumps(obj))
很簡單, 就是以分頁方式查詢 Navblocks 資料實體, 然後將其四個欄位資料包裝成 json 傳回前端而已. 增修刪部分由 /add_navblock, /update_navblock, /remove_navblock 路徑負責, 其類別如下 :
class add_navblock(webapp2.RequestHandler):
def post(self):
name=self.request.get("name")
display=self.request.get("display")
if display=="True":
display=True
else:
display=False
#check entity if exist
block=m.Navblocks.get_by_key_name(name)
if block: #already exist
result='{"status":"failure","reason":"導覽區塊已存在!"}'
else: #new block
navblock=m.Navblocks(key_name=name,name=name,
title=self.request.get("title"),
sequence=int(self.request.get("sequence")),
display=display
)
navblock.put()
result='{"status":"success"}'
self.response.out.write(result)
class update_navblock(webapp2.RequestHandler):
def post(self):
name=self.request.get("name")
display=self.request.get("display")
if display=="True":
display=True
else:
display=False
#get entity from store
block=m.Navblocks.get_by_key_name(name)
if block: #entity exist
block.title=self.request.get("title")
block.sequence=int(self.request.get("sequence"))
block.display=display
block.put()
result='{"status":"success"}'
else: #block not existed
result='{"status":"failure","reason":"導覽區塊不存在!"}'
self.response.out.write(result)
class remove_navblock(webapp2.RequestHandler):
def post(self):
name=self.request.get("name")
#get entity from store
block=m.Navblocks.get_by_key_name(name)
if block: #entity exist
db.delete(block)
#delete navlinks belong to this navblock
query=m.Navlinks.all()
links=query.filter("block_name",name)
for link in links:
db.delete(link)
result='{"status":"success"}'
else: #block not existed
result='{"status":"failure","reason":"導覽區塊不存在!"}'
self.response.out.write(result)
這都是從上一篇關於標頭連結 headerlinks 的文章中複製而來修改的, 比較不同的有兩處, 一是在 /add_navblock 部分, 因為前端傳來的 display 是 "True" 與 "False" 字串, 因此使用 if 判斷將其轉為 boolean 值. 二是在 /remove_navblock 部分, 除了刪除此區塊外, 還要同時刪除屬於此區塊的 Navlinks 資料實體.
而 Navlinks 管理部分, 同樣先在 model.py 新增一個 list_navlinks 實體 :
systab=Systabs(key_name="list_navlinks",tab_name="list_navlinks",
tab_label=u"導覽連結",tab_link="/list_navlinks",tab_order=5,tab_admin=True)
systab.put()
此頁籤會從路徑 list_navlinks 載入資料, 然後在 main.py 中新增此路徑 list_navlinks 的處理類別 :
class list_navlinks(webapp2.RequestHandler):
def get(self):
blocks=m.Navblocks.all()
info=[]
for b in blocks:
block={}
block["block_name"]=b.name
block["block_title"]=b.title
#block["block_title"]=b.title + " (" + b.name + ")"
info.append(block)
url="templates/list_navlinks.htm"
path=os.path.join(os.path.dirname(__file__), url)
content=template.render(path,{"info":info})
self.response.out.write(content)
它所渲染的網頁 list_navlinks.htm 內容如下, 係參考上一篇文章中的 list_headerlinks.htm 修改而來 :
{% extends "jqueryeasyui.htm" %}
{% block style %}
body {font: 80% "Trebuchet MS", sans-serif; margin: 50px;}
{% endblock%}
{% block body %}
<!--導覽連結 sys_nav_links 列表-->
<table id="sys_nav_links" title="導覽連結列表" style="width:auto" data-options="tools:'#nav_links_tools'"></table>
<div id="nav_links_tools">
<a href="#" id="add_nav_link" class="icon-add" title="新增"></a>
<a href="#" id="edit_nav_link" class="icon-edit" title="編輯"></a>
<a href="#" id="remove_nav_link" class="icon-remove" title="刪除"></a>
<a href="#" id="reload_nav_links" class="icon-reload" title="重新載入"></a>
</div>
<!--新增&編輯 Navlinks 表單對話框-->
<div id="nav_link_dialog" class="easyui-dialog" title="新增連結" style="width:360px;height:290px;" data-options="closed:'true',buttons:'#nav_link_buttons'">
<form id="nav_link_form" method="post" style="padding:10px">
<div style="margin:5px">
<label style="width:60px;display:inline-block;">名稱 : </label>
<input name="name" id="link_name" type="text" class="easyui-textbox" data-options="missingMessage:'此欄位必須為英數字組合',required:true,readonly:false" style="width:230px">
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">標題 : </label>
<input name="title" type="text" class="easyui-textbox" data-options="missingMessage:'此欄位為必填',required:true" style="width:230px">
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">網址 : </label>
<input name="url" type="text" class="easyui-textbox" data-options="missingMessage:'此欄位為必填',required:true" style="width:230px">
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">目標 : </label>
<select id="target" name="target" class="easyui-combobox" data-options="panelHeight:'auto'">
<option value="_self">_self</option>
<option value="_blank">_blank</option>
</select>
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">順序 : </label>
<input id="nav_link_sequence" name="sequence">
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">區塊 : </label>
<select name="block_name" class="easyui-combobox" data-options="missingMessage:'此欄位為必填',required:true,panelHeight:'auto'">
{% for b in info %}
<option value="{{ b.block_name }}">{{ b.block_title }}</option>
{% endfor %}
</select>
</div>
<div style="margin:5px">
<label style="width:60px;display:inline-block;">提示 : </label>
<input name="hint" type="text" class="easyui-textbox" style="width:230px">
<input type="hidden" id="nav_link_op" value="">
</div>
</form>
</div>
<div id="nav_link_buttons" style="padding-right:15px;">
<a href="#" id="clear_nav_link" class="easyui-linkbutton" iconCls="icon-clear" style="width:90px">重設</a>
<a href="#" id="save_nav_link" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">確定</a>
</div>
<script>
$(function(){
//導覽連結 sys_nav_links
$('#sys_nav_links').datagrid({
columns:[[
{field:'name',title:'name',sortable:true},
{field:'title',title:'title',sortable:true},
{field:'url',title:'url',sortable:true},
{field:'target',title:'target',sortable:true},
{field:'sequence',title:'sequence',align:'center',sortable:true},
{field:'block_name',title:'block_name',align:'center',sortable:true},
{field:'hint',title:'hint',sortable:true}
]],
url:"/get_navlinks",
method:"post",
singleSelect:true,
rownumbers:true
});
$("#nav_link_sequence").numberspinner({
min:0,
max:99,
increment:1,
value:"0",
required:true,
missingMessage:'此欄位為必填'
});
$("#clear_nav_link").bind("click",function(){
$("#nav_link_form")[0].reset();
});
$("#save_nav_link").bind("click",function(){
var op=$("#nav_link_op").val(); //判斷是新增或修改
if (op=="update") {
var row=$("#sys_nav_links").datagrid("getSelected");
if (row.name=="home" || row.name=="logout") {
$("#nav_link_dialog").dialog('close');
$.messager.alert("訊息","此為系統連結不可更改!","error");
return;
}
var url="/update_navlink";
}
else {var url="/add_navlink";}
$("#nav_link_form").form("submit",{
url:url,
method:"post",
success:function(data){
var data=eval('(' + data + ')');
$("#nav_link_dialog").dialog("close");
if (data.status==="success") {
$("#sys_nav_links").datagrid("reload");
}
else {
$.messager.alert("訊息",data.reason,"error");
}
}
});
});
$("#add_nav_link").bind("click",function(){
$("#nav_link_dialog").dialog("open").dialog("setTitle","新增連結");
$("#link_name").textbox({"readonly":false}); //for adding
$("#nav_link_form").form("clear");
$("#nav_link_op").val("add");
$('#target').combobox('setValue','_self');
});
$("#edit_nav_link").bind("click",function(){
var row=$("#sys_nav_links").datagrid("getSelected");
if (row) {
$("#nav_link_dialog").dialog("open").dialog("setTitle","編輯連結");
$("#nav_link_form").form("load",row);
$("#link_name").textbox({"readonly":true});
$("#nav_link_op").val("update");
}
else {$.messager.alert("訊息","請先選取要編輯的連結!","error");}
});
$("#remove_nav_link").bind("click",function(){
var row=$("#sys_nav_links").datagrid("getSelected");
if (row) {
var params={name:row.name};
$.messager.confirm("確認","確定要刪除這個連結嗎?",function(btn){
if (btn){
if (row.title=="首頁" || row.title=="登出") {
$.messager.alert("訊息","此為系統連結不可刪除!","error");
return;
}
var callback=function(data){
if (data.status==="success"){
$("#sys_nav_links").datagrid("reload");
}
else {$.messager.alert("訊息",data.reason,"error");}
};
$.post("/remove_navlink",params,callback,"json");
}
})
}
});
$("#reload_nav_links").bind("click",function(){
$("#sys_nav_links").datagrid("load");
});
});
</script>
{% endblock%}
這跟 list_headerlinks.htm 較不一樣的是多了一個 block_name 欄位, 用來標示此連結所屬之區塊. CRUD 操作部分, 此網頁模板會從 /get_navlinks 路徑取得 json 資料來源, 其路徑處理類別如下, 是從上一篇文章的 /get_headerlinks 複製來修改的 :
class get_navlinks(webapp2.RequestHandler):
def post(self):
page=self.request.get("page")
rows=self.request.get("rows")
sort=self.request.get("sort")
order=self.request.get("order")
if len(page):
page=int(page)
else:
page=1
if len(rows):
rows=int(rows)
else:
rows=10
if not len(sort):
sort="sequence"
if not len(order):
order="asc"
query=m.Navlinks.gql("ORDER BY %s %s" % (sort, order))
count=query.count()
links=query.fetch(rows, (page-1)*rows)
rows=[] #for storing objects
for h in links:
link={"name":h.name,
"title":h.title,
"url":h.url,
"target":h.target,
"sequence":h.sequence,
"block_name":h.block_name,
"hint":h.hint}
rows.append(link)
obj={"total":count,"rows":rows} #Easyui datagrid json format
self.response.headers["Content-Type"]="application/json"
self.response.out.write(json.dumps(obj))
class add_navlink(webapp2.RequestHandler):
def post(self):
name=self.request.get("name")
#check entity if exist
link=m.Navlinks.get_by_key_name(name)
if link: #already exist
result='{"status":"failure","reason":"連結名稱已存在!"}'
else: #new link
navlink=m.Navlinks(key_name=name,name=name,
title=self.request.get("title"),
url=self.request.get("url"),
target=self.request.get("target"),
sequence=int(self.request.get("sequence")),
block_name=self.request.get("block_name"),
hint=self.request.get("hint")
)
navlink.put()
result='{"status":"success"}'
self.response.out.write(result)
class update_navlink(webapp2.RequestHandler):
def post(self):
name=self.request.get("name")
#get entity from store
link=m.Navlinks.get_by_key_name(name)
if link: #entity exist
link.title=self.request.get("title")
link.url=self.request.get("url")
link.target=self.request.get("target")
link.sequence=int(self.request.get("sequence"))
link.block_name=self.request.get("block_name")
link.hint=self.request.get("hint")
link.put()
result='{"status":"success"}'
else: #link not existed
result='{"status":"failure","reason":"連結不存在!"}'
self.response.out.write(result)
class remove_navlink(webapp2.RequestHandler):
def post(self):
name=self.request.get("name")
#get entity from store
link=m.Navlinks.get_by_key_name(name)
if link: #entity exist
db.delete(link)
result='{"status":"success"}'
else: #link not existed
result='{"status":"failure","reason":"連結不存在!"}'
self.response.out.write(result)
OK, 終於大功告成了! 這篇測試紀錄其實在除夕前就開始寫, 過年忙這忙那, 斷斷續續寫到今天年初二才完成. 這樣整個 CMS on GAE 的主體架構移植大致完成, 剩下的就是一些系統功能, 例如檔案上傳下載, 留言板, 系統管理設定等等, 過完年我可能要開始玩 App Inventor, 既然已經證實 GAE 也可以實作 CMS, 剩下的功能就不用急, 可以慢慢做.
實際測試連結如下 :
測試 5 : http://jqueryeasyui.appspot.com/main_5 (下載原始碼) (備用下載點)
"NeedIndexError: The index for this query is not ready to serve"
NeedIndexError: The index for this query is not ready to serve. See the Datastore Indexes page in the Admin Console. The suggested index for this query is: - kind: Navblocks properties: - name: display - name: sequence
解決辦法參考下列這篇 :
# How to solve the Index warning on GAE?
就是將 index.yaml 檔案的內容全部殺掉 (保留 remark), 上傳後重新整理網頁即可.
其他參考資料 :
# add “readonly” to <input > (jQuery)
# 怎么设置combobox的默认值 How to set the default value of combobox
沒有留言 :
張貼留言