From 0ede26497a9e018cbd68e2f4120dcb44a21f4715 Mon Sep 17 00:00:00 2001 From: Nick Retallack Date: Tue, 6 Oct 2009 12:32:59 -0700 Subject: [PATCH] trying out resource routing. Works decently. Added lots of notes. --- floof/config/routing.py | 40 +++++++++++++++++++++++++++++++++++----- floof/controllers/art.py | 33 ++++++++++++++++++++------------- floof/controllers/tag.py | 20 +++++++++++++++----- floof/lib/helpers.py | 1 + floof/model/art.py | 3 +++ floof/templates/art/show.mako | 16 +++++++++++----- floof/templates/base.mako | 2 +- floof/templates/index.mako | 2 +- floof/todo.txt | 6 +++++- 9 files changed, 92 insertions(+), 31 deletions(-) diff --git a/floof/config/routing.py b/floof/config/routing.py index 6ec64e4..9c3da14 100644 --- a/floof/config/routing.py +++ b/floof/config/routing.py @@ -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') diff --git a/floof/controllers/art.py b/floof/controllers/art.py index f08cd60..7ca1285 100644 --- a/floof/controllers/art.py +++ b/floof/controllers/art.py @@ -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)) diff --git a/floof/controllers/tag.py b/floof/controllers/tag.py index 3b69ed6..d8e46f3 100644 --- a/floof/controllers/tag.py +++ b/floof/controllers/tag.py @@ -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)) + diff --git a/floof/lib/helpers.py b/floof/lib/helpers.py index 28e9176..c8a1251 100644 --- a/floof/lib/helpers.py +++ b/floof/lib/helpers.py @@ -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 * diff --git a/floof/model/art.py b/floof/model/art.py index c3fba3a..9ff00c5 100644 --- a/floof/model/art.py +++ b/floof/model/art.py @@ -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 diff --git a/floof/templates/art/show.mako b/floof/templates/art/show.mako index 715b9d1..262f1cb 100644 --- a/floof/templates/art/show.mako +++ b/floof/templates/art/show.mako @@ -5,24 +5,30 @@

Viewing Art

% 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: -x +${h.form(h.url("art_tag", art_id=c.art.id, id=tag.id), method="delete")} +${h.submit('delete', 'X')} ${tag} +${h.end_form()} % endfor

What do you think?

+${h.form (h.url("rate_art", id=c.art.id), method="put")} % for score,text in sorted(Rating.options.items()): -${text} + % endfor +${h.end_form()} % endif diff --git a/floof/templates/base.mako b/floof/templates/base.mako index 654b8b3..13126f7 100644 --- a/floof/templates/base.mako +++ b/floof/templates/base.mako @@ -11,7 +11,7 @@ Home % if c.user: -| Add Art +| Add Art | Your Searches ## | Your Page % endif diff --git a/floof/templates/index.mako b/floof/templates/index.mako index af95f7a..8ee86e6 100644 --- a/floof/templates/index.mako +++ b/floof/templates/index.mako @@ -3,7 +3,7 @@