trying out resource routing. Works decently. Added lots of notes.
authorNick Retallack <nickretallack@gmil.com>
Tue, 6 Oct 2009 19:32:59 +0000 (12:32 -0700)
committerNick Retallack <nickretallack@gmil.com>
Tue, 6 Oct 2009 19:32:59 +0000 (12:32 -0700)
floof/config/routing.py
floof/controllers/art.py
floof/controllers/tag.py
floof/lib/helpers.py
floof/model/art.py
floof/templates/art/show.mako
floof/templates/base.mako
floof/templates/index.mako
floof/todo.txt

index 6ec64e4..9c3da14 100644 (file)
@@ -12,6 +12,7 @@ def make_map():
     map = Mapper(directory=config['pylons.paths']['controllers'],
                  always_scan=config['debug'], explicit=True)
     map.minimization = False
+
     # explicit = True disables a broken feature called "route memory",
     # where it adds everything matched in the current request as default variables
     # for the next one.  This is wrong because it doesn't invalidate things lower down in
@@ -20,6 +21,11 @@ def make_map():
 
     require_POST = dict(conditions={'method': ['POST']})
 
+    # get rid of trailing slashes
+    map.redirect('/*(url)/', '/{url}',
+                 _redirect_code='301 Moved Permanently')
+
+
     # The ErrorController route (handles 404/500 error pages); it should
     # likely stay at the top, ensuring it can always be resolved
     map.connect('/error/{action}', controller='error')
@@ -39,12 +45,36 @@ def make_map():
     map.connect('/users/{name}', controller='users', action='view')
 
     # Art stuff
-    map.connect('/art/new', controller='art', action='new')
-    map.connect('/art/upload', controller='art', action='upload')
-    map.connect('show_art', '/art/{id}', controller='art', action='show')
-    map.connect('/art/{id}/tag', controller='art', action='tag')
+    art = map.resource('art','art', controller="art", member={'rate':'PUT'})
+    # wow, it even works if you name the plural and singular the same thing.
+    # Resources documented here: http://routes.groovie.org/manual.html#restful-services
+    # It seems the first parameter (singular) is only ever used in route names, e.g. url('kitten', id=5).
+    # The second parameter, plural, is used everywhere else by default: in the url, controller name, 
+    # and the route name for the collection.  e.g. url('kittens') -> '/kittens' -> kittens.index().
+    # Since our controllers have singular names, we'll have to override this every time with the 'controller' parameter.
+    # Even singular routes use the plural in urls.  url('kitten', id=5) -> '/kittens/5'.
+    # And it appears that if the singular and plural are the same, either will match, so no harm done.
+    # It does mean, however, that if you have a None id accidentally, url('art', id=None) you'll get the same thing    
+    # as url('art').  I mean, you might have wanted a singular but you got a plural route instead.
+    
+    map.resource('tag','tags', controller="tag",
+        parent_resource=dict(member_name='art', collection_name='art'))
+        # Yeah, parent resources are specified kinda dumb-ly.  Would be better if you could pass in the
+        # real parent resource instead of mocking it up with a silly dict.  We should file a feature request.
+        
+    # I think resources is the right way to go for most things.  It ensures all of our actions have the right
+    # methods on them, at least.  It does require the use of silly _method="delete" post parameters though.
+    
+    # One sticking point though is, it'll happily allow you to add any formatting string you want, like art/1.json
+    # I wonder if there's a way to place requirements on that, or disable it until we actually have formats.
+    # It just serves the same action as usual but with a format argument in the context.
+            
+    # map.connect('/art/new', controller='art', action='new')
+    # map.connect('/art/upload', controller='art', action='upload')
+    # map.connect('show_art', '/art/{id}', controller='art', action='show')
+    # map.connect('/art/{id}/tag', controller='art', action='tag')
 
-    map.connect('/tag/{id}/delete', controller='tag', action='delete')
+    map.connect('/tag/{id}/delete', controller='tag', action='delete')
 
     map.connect('search', '/search', controller='search', action='index')
     map.connect('/search/list', controller='search', action='list')
index f08cd60..7ca1285 100644 (file)
@@ -1,14 +1,14 @@
 import logging
 
 from pylons import request, response, session, tmpl_context as c, h
-from pylons.controllers.util import abort, redirect_to
-
+from pylons.controllers.util import abort, redirect
+from pylons import url
 from floof.lib.base import BaseController, render
 
 log = logging.getLogger(__name__)
 
 import elixir
-from floof.model.art import Art
+from floof.model.art import Art, Rating
 
 class ArtController(BaseController):
     def __before__(self, id=None):
@@ -26,7 +26,7 @@ class ArtController(BaseController):
         return render("/art/new.mako")
 
     # TODO: login required
-    def upload(self):
+    def create(self):
         Art(uploaded_by=c.user, **request.params)
         elixir.session.commit()
         redirect_to(controller="main", action="index")
@@ -37,16 +37,23 @@ class ArtController(BaseController):
             c.your_score = c.art.user_score(c.user)
         return render("/art/show.mako")
         
-    # TODO: login required
-    def tag(self, id):
-        # c.art = h.get_object_or_404(Art, id=id)
-        c.art.add_tags(request.params["tags"], c.user)
-        elixir.session.commit()
-        redirect_to('show_art', id=c.art.id)
-    
+    # TODO: login required
+    def tag(self, id):
+        # c.art = h.get_object_or_404(Art, id=id)
+        c.art.add_tags(request.params["tags"], c.user)
+        elixir.session.commit()
+        redirect_to('show_art', id=c.art.id)
+    # 
     # TODO: login required
     def rate(self, id):
         # c.art = h.get_object_or_404(Art, id=id)
-        c.art.rate(request.params["score"], c.user)
+        score = request.params.get("score")
+        if score and score.isnumeric():
+            score = int(score)
+        else:
+            score = Rating.reverse_options.get(score)
+        
+        c.art.rate(score, c.user)
         elixir.session.commit()
-        redirect_to('show_art', id=c.art.id)
+            
+        redirect(url('art', id=c.art.id))
index 3b69ed6..d8e46f3 100644 (file)
@@ -1,20 +1,30 @@
 import logging
 
-from pylons import request, response, session, tmpl_context as c
-from pylons.controllers.util import abort, redirect_to
+from pylons import request, response, session, tmpl_context as c, h
+from pylons.controllers.util import abort, redirect
 
 from floof.lib.base import BaseController, render
+from pylons import url
 
 log = logging.getLogger(__name__)
 
 import elixir
-from floof.model.art import Tag
+from floof.model.art import Art, Tag
 
 class TagController(BaseController):
 
-    def delete(self, id):
+    # TODO: login required
+    def delete(self, art_id, id):
         tag = Tag.get(id)
         if tag:
             elixir.session.delete(tag)
             elixir.session.commit()
-        redirect_to(request.referrer)
\ No newline at end of file
+        redirect(url('art', id=art_id))
+        
+    # TODO: login required
+    def create(self, art_id):
+        c.art = h.get_object_or_404(Art, id=art_id)
+        c.art.add_tags(request.params["tags"], c.user)
+        elixir.session.commit()
+        redirect(url('art', id=c.art.id))
+
index 28e9176..c8a1251 100644 (file)
@@ -8,6 +8,7 @@ available to Controllers. This module is available to both as 'h'.
 # from webhelpers.html.tags import checkbox, password
 from webhelpers import *
 from routes import url_for, redirect_to
+from pylons import url
 
 # Scaffolding helper imports
 from webhelpers.html.tags import *
index c3fba3a..9ff00c5 100644 (file)
@@ -109,6 +109,9 @@ class Rating(Entity):
     art = ManyToOne('Art', ondelete='cascade')
     rater = ManyToOne('User', ondelete='cascade')
     score = Field(Integer)
+    
+    # @score.setter
+    # def score(self, value):    
         
     options = {-1:"sucks", 0:"undecided", 1:"good", 2:"great"}
     default = 0
index 715b9d1..262f1cb 100644 (file)
@@ -5,24 +5,30 @@
 <h1>Viewing Art</h1>
 
 % if c.user:
-${h.form (h.url_for (controller='art', action='tag', id=c.art.id), multipart=True)}
+${h.form (h.url("art_tags", art_id=c.art.id))}
 Add Some Tags: ${h.text('tags')}
 ${h.submit('submit', 'Tag!')}
 ${h.end_form()}
 
 % for tag in c.art.tags:
-<a href="${url(controller='tag', action='delete', id=tag.id)}">x</a>
+${h.form(h.url("art_tag", art_id=c.art.id, id=tag.id), method="delete")}
+${h.submit('delete', 'X')}
 <a href="${url(controller='search', action='index', query=tag)}">${tag}</a>
+${h.end_form()}
 % endfor
 
 <h2>What do you think?</h2>
+${h.form (h.url("rate_art", id=c.art.id), method="put")}
 % for score,text in sorted(Rating.options.items()):
-<a href="${h.url_for(controller='art', action='rate', id=c.art.id)}?score=${score}" \
+
 % if c.your_score == score:
-class="selected" \
+${h.submit('score', text, class_="selected")}
+% else:
+${h.submit('score', text)}
 % endif
->${text}</a> 
+
 % endfor
+${h.end_form()}
 % endif
 
 <img class="full" src="${c.art.get_path()}">
index 654b8b3..13126f7 100644 (file)
@@ -11,7 +11,7 @@
 <a href="${h.url_for("/")}">Home</a>
 
 % if c.user:
-| <a href="${h.url_for(controller="art", action="new")}">Add Art</a>
+| <a href="${h.url("new_art")}">Add Art</a>
 | <a href="${h.url_for(controller="search", action="list")}">Your Searches</a>
 ## | <a href="${h.url_for("/users/"+c.user}">Your Page</a>
 % endif
index af95f7a..8ee86e6 100644 (file)
@@ -3,7 +3,7 @@
 
 <ul class="artwork-grid">
     % for artwork in c.artwork:
-    <li><a href="${h.url_for(controller="art", action="show", id=artwork.id)}">
+    <li><a href="${h.url("art", id=artwork.id)}">
         <img width="180" src="${artwork.get_path()}">
     </a></li>
     % endfor
index f9a7cd0..85b3761 100644 (file)
@@ -1 +1,5 @@
--- uploading files
\ No newline at end of file
+- new art:
+    if hash exists, do not create another record.
+    
+- search syntax:
+    railroad it
\ No newline at end of file