谷歌应用引擎中ReferenceProperty关系

1 投票
4 回答
2612 浏览
提问于 2025-04-15 13:17

我正在尝试使用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 个回答

0

我不太确定你遇到了什么问题。你列出的内容看起来都没问题——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对象的键。

1

我只是想给其他人解答这个问题,因为你可能已经弄明白了:

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()
2

如果某个操作会产生副作用,比如改变存储(例如创建一个新对象),那么它不应该使用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时就能获取到它。

撰写回答