谷歌应用引擎中ReferenceProperty关系
我正在尝试使用ReferenceProperty来关联我的模型,但运气不太好。我有三个层级:组(Group)、主题(Topic),然后是优点(Pros)和缺点(Cons)。也就是说,一个组可以包含很多主题,而每个主题下又可以有很多优点和缺点。
我可以顺利地存储新的组,但我不知道如何在这些组下面存储主题。我想在每个组下面放一个“新主题”的链接,点击后可以跳转到一个简单的表单(现在只需要一个输入框)。显然,网址需要包含某种方式来引用组的ID或其他信息。
以下是我的模型:
class Groups(db.Model):
group_user = db.UserProperty()
group_name = db.StringProperty(multiline=True)
group_date = db.DateTimeProperty(auto_now_add=True)
class Topics(db.Model):
topic_user = db.UserProperty()
topic_name = db.StringProperty(multiline=True)
topic_date = db.DateTimeProperty(auto_now_add=True)
topic_group = db.ReferenceProperty(Groups, collection_name='topics')
class Pro(db.Model):
pro_user = db.UserProperty()
pro_content = db.StringProperty(multiline=True)
pro_date = db.IntegerProperty(default=0)
pro_topic = db.ReferenceProperty(Topics, collection_name='pros')
class Con(db.Model):
con_user = db.UserProperty()
con_content = db.StringProperty(multiline=True)
con_date = db.IntegerProperty(default=0)
con_topic = db.ReferenceProperty(Topics, collection_name='cons')
还有一个函数,用于实际显示组列表的页面,然后在它们下面显示主题:
class Summary(webapp.RequestHandler):
def get(self):
groups_query = Groups.all()
groups = groups_query.fetch(1000)
template_values = {
'groups': groups,
}
path = os.path.join(os.path.dirname(__file__), 'summary.html')
self.response.out.write(template.render(path, template_values))
最后是html代码:
<html>
<body>
<a href="/newgroup">New Group</a>
<br>
{% for group in groups %}
<font size="24">{{ group.group_name|escape }}</font><br> by <b>{{ group.group_user }}</b> at <b>{{ group.group_date }}</b> {{ group.raw_id }}
<br>
<a href="/newtopic?id={{group.key.id}}" >New topice </a>
<br>
<blockquote>
{{ topics.topics_name }}
</blockquote>
{% endfor %}
</body>
</html>
4 个回答
我不太确定你遇到了什么问题。你列出的内容看起来都没问题——ReferenceProperties的设置也符合你的描述。唯一我能看到的问题是,在你的模板中,你提到了一个叫“topics”的变量,但这个变量并没有在任何地方定义,而且你也没有在任何地方遍历这个组的主题。你可以这样做:
<html>
<body>
<a href="/newgroup">New Group</a>
<br>
{% for group in groups %}
<font size="24">{{ group.group_name|escape }}</font><br> by <b>{{ group.group_user }}</b> at <b>{{ group.group_date }}</b> {{ group.raw_id }}
<br>
<a href="/newtopic?id={{group.key.id}}" >New topice </a>
<br>
Topics:
<ul>
{% for topic in group.topics %}
<li>{{topic.topic_name}}</li>
{% endfor %}
</ul>
{% endfor %}
</body>
</html>
要创建一个新主题,只需使用构造函数,并传入所需的参数:
mytopic = Topic(topic_name="foo", topic_group=somegroup)
这里,somegroup应该是一个Group对象,或者是一个Group对象的键。
我只是想给其他人解答这个问题,因为你可能已经弄明白了:
class NewTopic(webapp.RequestHandler): def get(self): groupId = self.request.get('group') # either get the actual group object from the DB and initialize topic with topic_group=object as in 'Nick Johnson's answer, or do as follows topic = Topic() topic.name = self.request.get("topicname") topic.reference = groupId topic.put()
如果某个操作会产生副作用,比如改变存储(例如创建一个新对象),那么它不应该使用HTTP的GET
请求——GET请求基本上只应该用来“读取”数据。这不是小题大做,而是HTTP协议的一个重要规则——浏览器、缓存、代理等都可以把GET请求当作只读操作来处理(比如可以缓存结果,如果缓存中有数据,就不再向服务器发送请求)。
如果要进行修改,应该使用HTTP的其他方法,比如POST(最常用,因为所有浏览器都能正确实现它),或者用于特定操作的PUT(用于创建新对象)和DELETE(用于删除对象)。我想你会选择POST来支持各种浏览器。
要从浏览器发送一个POST请求,你需要用到一些JavaScript技巧,或者使用一个简单的表单,设置方法为post——为了简单起见,我假设你会用后者。
如果你使用的是Django 1.0(现在应用引擎支持这个版本),它有自己的机制来创建、验证和接受基于模型的表单。其他框架也有类似的高级功能。
如果你想避免使用“复杂”的框架,你需要手动实现HTML表单的模板,通过某种URL分发(例如在app.yaml中)将其指向你自己实现的处理程序,处理程序中包含def post(self):
,从请求中获取数据,验证数据,创建新对象,保存它,并显示一个确认页面。
你对这个过程的哪部分不清楚?你的问题标题专注于引用属性,但我不太确定它们具体给你带来了什么问题——从你问题的描述来看,你似乎对它们的理解是正确的。
编辑:提问者在评论中澄清了他的困惑是如何让类似于:
"<a href="/newtopic?id={{group.key.id}}" >New topic </a>"
的内容正常工作。实现这个有不止一种方法。如果newtopic的URL由一个静态表单提供,那么该表单的post“动作”处理程序可以通过Referer:
头部获取到id=
(这是个臭名昭著但无法修复的拼写错误),但这种方法有点笨重且不稳定。更好的方法是让newtopic的URI由一个处理程序提供,该处理程序的def get
从请求中获取id=
并将其插入到生成的表单模板中——例如,放在一个隐藏的输入字段里。让该表单的模板包含(除了其他字段):
<INPUT TYPE=hidden NAME=thegroupid VALUE={{ theid }}> </INPUT>
将theid
放入你渲染该模板的上下文中,这样在处理接收表单的def post
时就能获取到它。