Question

I'm trying to get my models related using ReferenceProperty, but not have a huge amount of luck. I have 3 levels: Group, Topic, then Pros, and Cons. As in a Group houses many topics, and within each topic could be many Pros and Cons.

I am able to store new Groups nice and fine, but I don't have any idea how to store topics underneath these groups. I want to link from a page with a link "New topic" underneath each group, that takes them to a simple form (1 field for now). Obviously the URL will need to have some sort of reference to the id of the group or something.

Here are my models:

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')    

And one function for the actual page I want to show the list of Groups, and then underneath their topics:

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

And finally the 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>
Was it helpful?

Solution

Something that has side effects, such as altering the store (by creating a new object for example) should NOT be an HTTP GET -- GET should essentially only do "read" operations. This isn't pedantry, it's a key bit of HTTP semantics -- browsers, caches, proxies, etc, are allowed to act on GET as read-only operations (for example by caching results and not passing a request to the server if they can satisfy it from cache).

For modifications, use HTTP verbs such as POST (most popular essentially because all browsers implement it correctly) or for specialized operations PUT (to create new objects) or DELETE (to remove objects). I assume you'll be going to use POST to support a variety of browsers.

To get a POST from a browser, you need either Javascript wizardy or a plain old form with method=post -- I'll assume the latter for simplicity.

If you're using Django 1.0 (which app engine supports now), it has its own mechanisms to make, validate and accept forms based on models. Other frameworks have their own similarly advanced layers.

If you want to avoid "rich" frameworks you'll have to implement by hand templates for your HTML forms, direct them (via some kind of URL dispatching, e.g. in app.yaml) to a handler of yours implementing with a def post(self):, get the data from the request, validate it, form the new object, put it, display some acknowledgment page.

What part or parts of the procedure are unclear to you? Your question's title focuses specifically on reference properties but I'm not sure what problem they are giving you in particular -- from the text of your question you appear to be on the right tack about them.

Edit: the OP has now clarified in a comment that his problem is how to make something like:

"<a href="/newtopic?id={{group.key.id}}" >New topic </a>" 

work. There's more than one way to do that. If the newtopic URL is served by a static form, the handler for the post "action" of that form could get back to that id= via the Referer: header (a notorious but unfixable mis-spelling), but that's a bit clunky and fragile. Better is to have the newtopic URI served by a handler whose def get gets the id= from the request and inserts it in the resulting form template -- for example, in a hidden input field. Have that form's template contain (among the other fields):

<INPUT TYPE=hidden NAME=thegroupid VALUE={{ theid }}> </INPUT>

put theid in the context with which you render that template, and it will be in the request that the def post of the action receiving the form finally gets.

OTHER TIPS

Just to answer the question for others as you probably figured this out:

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

Thankyou for the reply.

Yeah I am aware of the get vs post. The class I posted was just to actually print all the Groups().

The issue I have is I'm unsure how I use the models to keep data in a sort of hierarchical fashion, with Groups > Topics > Pros/Cons.

Grabbing data is simple enough and I am using:

class NewGroupSubmit(webapp.RequestHandler):
    def post(self):

        group = Groups()
        if users.get_current_user():
            group.group_user = users.get_current_user()     
        group.group_name = self.request.get('groupname')

        group.put()
        self.redirect('/summary')

I need another function to add a new topic, that stores it within that group. So lets say a group is "Cars" for instance; the topics might be "Ferrari", "Porsche", "BMW", and then pros/cons for each topic. I realise I'm being a little vague, but it's because I'm very new to relational databasing and not quite used to the terminology.

I'm not quite sure what problem you're having. Everything you list looks fine - the ReferenceProperties are set up according to what one would expect from your dscription. The only problem I can see is that in your template, you're referring to a variable "topics", which isn't defined anywhere, and you're not iterating through the topics for a group anywhere. You can do that like this:

<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>

To create a new topic, just use the constructor, passing in the required arguments:

mytopic = Topic(topic_name="foo", topic_group=somegroup)

Here, somegroup should be either a Group object, or a key for a Group object.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top