new feature in trunk

668 views
Skip to first unread message

Massimo Di Pierro

unread,
Oct 20, 2013, 11:57:00 PM10/20/13
to web...@googlegroups.com
It is a recurrent problem that is displays tree-like structures like threaded comments. For example:

db.define_table('post',
                Field('parent_id','reference post'),
                Field('body'))

where each comment has a body and a parent_id (parent_id==None for the root comment(s))
We can populate the comments with some dummy data:

def make_up_data():
    import random, uuid
    ids = [None]
    for k in range(100):
        ids.append(db.post.insert(parent_id=random.choice(ids),
                                  body=str(uuid.uuid4())))
        if k==0:
            ids.pop(None)
if db(db.post).isempty(): make_up_data()

The new feature in trunk allows you to select the comments are organized them into trees.

   roots = db(db.post).select().as_trees()

This returns a list of parent nodes. Each not stores its own children, recursively.

Now you can print them all using a tree traversal:

    def show(row,n=0):
        return '  '*n+row.body+'\n'+''.join(show(c,n+1) for c in row.children)
    print show(roots[0])

Notice you can specify the name of the parent field:

    roots = db(db.post).select().as_trees(parent_name="parent_id")

Please let me know if you think this can be improved.

Mirko

unread,
Oct 21, 2013, 3:48:18 AM10/21/13
to web...@googlegroups.com
Hi Massimo,
this is a very promising feature !

My quick two cents:
Why not turn this generic by replacing the 'body' field with a 'node_name' field and add an attribute node table ?
For example: 

db.define_table('node',
                Field('parent_id','reference node'),
                Field('node_name','string'))
and
db.define_table('attribute',
                Field('node_id','reference node'),
                Field('key','string'),
                Field('value','string'))
The tree definition above needs to create a fake root node, forces a given node to belong to only one parent node and is efficient when traversing tree from leaf to trunk but for a given node the children node(s) are unknown.

The tree definition below could be a better solution, but may require extra work to keep references integrity especially when moving or deleting nodes:
db.define_table('node',
                Field('parent_id','list:reference node'),
                Field('child_id','list:reference node'),  // sorting this may permit to manage siblings
                Field('node_name','string'))

Eventually a delirious attribute table evolution might be to have a type field (string, integer, reference, ....) and validator per node attribute, but I can't figure out how to do that :)


Thanks again for this new feature,

Mirko,


Niphlod

unread,
Oct 21, 2013, 6:55:44 AM10/21/13
to web...@googlegroups.com
ouch.... the easiest model to update, the worst to query. 
I was going to post a plugin for threaded comments but then life kicked in with lots of other requirements, and then other things got priority in web2py.
I don't think this will be compatible with what I've done 'cause I use a totally different model, but alas, this is better than no plugin at all.


On Monday, October 21, 2013 5:57:00 AM UTC+2, Massimo Di Pierro wrote:

Massimo Di Pierro

unread,
Oct 21, 2013, 3:26:44 PM10/21/13
to web...@googlegroups.com
Actually mine was an example. as_trees() does not dictate a model. The only requirement is that the model must have a self referencing field. You can call it any way you like it. You can pass its name as first argument of as_trees(). 

Richard Vézina

unread,
Oct 21, 2013, 3:30:04 PM10/21/13
to web2py-users
That cool Massimo!

Thanks for this...

Richard


--
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
---
You received this message because you are subscribed to the Google Groups "web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web2py+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

webpypy

unread,
Oct 22, 2013, 1:56:52 AM10/22/13
to web...@googlegroups.com

very nice ....

what about the display of the tree in the view ? maybe as a column in a table.

Dave S

unread,
Oct 22, 2013, 1:41:43 PM10/22/13
to web...@googlegroups.com
On Monday, October 21, 2013 10:56:52 PM UTC-7, webpypy wrote:

very nice ....

what about the display of the tree in the view ? maybe as a column in a table.

I would think that nested ul's would also be a natural choice.

/dps
 

Dave S

unread,
Oct 22, 2013, 1:46:28 PM10/22/13
to web...@googlegroups.com
 and with an automatic way to turn them to links ...    =8-O  

(I suppose we don't want to have too many over-specialized helper functions, and it wouldn't be hard to use MDP's example to fill in the links.)

/dps

Arnon Marcus

unread,
Oct 24, 2013, 4:35:38 AM10/24/13
to web...@googlegroups.com
How would this work internally?
What queries would be generated?
How many queries would be generated?
At what points in time would queries be "executed"?
Would that be a "lazy" execution?

We have many such tables in our project, so this is a big interest of ours to have this work as best it can.
The "straight forward" way of querying hierarchical queries is horribly inefficient.
We already have a case that ends-up generating over 7K queries for a single view...
Even with connection-pooling it takes almost 30 seconds.
After moving postgres to a ramdisk, and adding a local PGBouncer server, that dropped to 14s.
But obviously this is not a solution.

The way we thought of solving that, is have other field(s) against which a single query could be issued, and then have the tree-structuring done in python.
Is this the approach this solution is taking? 

Nguyen Minh Tuan

unread,
Dec 31, 2013, 10:53:09 AM12/31/13
to web...@googlegroups.com
I tried to use with MS SQL but this function seem error!

Alex Glaros

unread,
Jun 1, 2016, 2:06:14 PM6/1/16
to web...@googlegroups.com
can someone show me syntax for use outside of the "return" statement?

In other words, I want to run and display data within a larger function than the example, that already has vars and data I need, and displays other data that needs to appear.

My model is self-referencing table with link to parent named: "taxonomy_column_parent_fk

In controller

roots = db(db.taxonomy_column).select().as_trees(parent_name="taxonomy_column_parent_fk")

In view

{{row,n=0}}
{{for c in roots:}}
   {{='  '*n+row.body+'\n'+''.join(show(c,n+1))}}
{{pass}}

Error

<type 'exceptions.TypeError'> 'int' object is not iterable


thanks

Alex Glaros

Anthony

unread,
Jun 1, 2016, 2:35:20 PM6/1/16
to web2py-users
No, you define a function like show() in Massimo's example, and then you just call that function:

{{=show(roots[0])}}

show() is called recursively. The default/initial value of "n" is 0 (you don't need to specify that), and the recursive calls automatically increment n by 1.

Anthony


On Wednesday, June 1, 2016 at 2:06:14 PM UTC-4, Alex Glaros wrote:
can someone show me syntax for use outside of the "return" statement?

In other words, I want to run and display data within a larger function than the example, that already has vars and data I need, and displays other data that needs to appear.

My model is self-referencing table with link to parent named: "taxonomy_column_parent_fk

In controller

roots = db(db.taxonomy_column).select().as_trees(parent_name="taxonomy_column_parent_fk")

In view

{{for c in roots:}}
   {{='  '*n+row.body+'\n'+''.join(show(c,n+1))}}
{{pass}}

Error

<type 'exceptions.NameError'> name 'n' is not defined


thanks

Alex Glaros

Alex Glaros

unread,
Jun 1, 2016, 3:11:31 PM6/1/16
to web2py-users
getting closer....

Controller

def show(row,n=0):
   return '  '*n+row.body+'\n'+''.join(show(c,n+1) for c in row.children)

def view_all_objects_in_a_taxonomy():
   roots = db(db.taxonomy_column).select().as_trees(parent_name="taxonomy_column_parent_fk")
   return locals()
    
View - view_all_objects_in_a_taxonomy

{{=show(roots[0])}

Error

<type 'exceptions.NameError'> name 'show' is not defined

Dave S

unread,
Jun 1, 2016, 3:58:32 PM6/1/16
to web2py-users

Do you need to move the def show into view_all_objects_in_a_taxonomy()?  That would make it one of the locals you are returning, no?

/dps

Alex Glaros

unread,
Jun 1, 2016, 5:16:47 PM6/1/16
to web2py-users
what would the syntax be Dave? I don't know how to move a def into another def...


Dave S

unread,
Jun 1, 2016, 6:51:17 PM6/1/16
to web2py-users


On Wednesday, June 1, 2016 at 2:16:47 PM UTC-7, Alex Glaros wrote:
what would the syntax be Dave? I don't know how to move a def into another def..


Cut, paste, and adjust the indentation.  As the Python docs say, "Programmer’s note: Functions are first-class objects. A “def” form executed inside a function definition defines a local function that can be returned or passed around. Free variables used in the nested function can access the local variables of the function containing the def. See section Naming and binding for details."  (Sec 7.6 Function Definitions, at
<URL:https://docs.python.org/2/reference/compound_stmts.html#function>

There's a recent example in the group at
<URL:https://groups.google.com/d/msg/web2py/h5vnE56IZrM/MnOZWVhcAwAJ>
(Minor amusing note: the Python function returned in that example... is used to generate Javascript)

/dps

Anthony

unread,
Jun 1, 2016, 8:01:28 PM6/1/16
to web2py-users
You've defined show() in a controller but are attempting to call it in a view without passing it to the view. Either pass show to the view, define it in a model, or put it in a module and import it in the view.

Anthony

Alex Glaros

unread,
Jun 3, 2016, 4:50:25 PM6/3/16
to web2py-users
how to pass it to the view? 


Alex Glaros

unread,
Jun 3, 2016, 5:42:12 PM6/3/16
to web...@googlegroups.com
okay, got this below to work but am confused about the resulting order data is displayed in.

RAW DATA IN TABLE

post.idpost.parent_idpost.body
1None<top dog - re...
21<2nd dog - re...
31< 3nd dog dow...
42<grandchild -...
51<record # 5, ...
62< record #6, ...

Here is the result:

This is the default/show1.html template

<top dog - record #1 points to nothing> <2nd dog - record #2 points to 1> <grandchild - record #4 but points to #2 > < record #6, points to 2> < 3nd dog down - record #3 directly points to record# 1> <record # 5, points to #1 top dog>

shouldn't < 3nd dog down - record #3 directly points to record# 1>  be positioned right after parent record #1?

CONTROLLER

def show1():
    def show(row,n=0):
        return '  '*n+row.body+'\n'+''.join(show(c,n+1) for c in row.children)
    roots = db(db.post).select().as_trees()
    return locals()

MODEL

db.define_table('post',
                Field('parent_id','reference post'),
                Field('body'))


VIEW

{{extend 'layout.html'}}
<h1>This is the default/show1.html template</h1>
{{=show(roots[0])}}

  • How to wrap a href URL around each individual data item?
  • Is the resulting data intended to be fed into a visualizer like fancyTree?

thanks

Alex
Reply all
Reply to author
Forward
0 new messages