RoutablePage

162 views
Skip to first unread message

Robert Slotboom

unread,
Jun 13, 2016, 12:09:56 PM6/13/16
to Wagtail support
Hi guys,

I’v got this routes in a routable page model:

@route(r'^$')
def about(self, request):
   
return TemplateResponse(
        request
,
       
self.get_template(request),
       
self.get_context(request)
   
)


@route(r'^lectures/$')
def lectures(self, request):
    cls
= Lecture
    context
= self.get_context(request)
    context
.update({'item_list': self.get_products(cls)})
   
return TemplateResponse(
        request
,
       
self.get_template(request),
        context
   
)

@route(r'^for-families/$')
def about_family(self, request):
   
pass

@route(r'^for-family/lectures/$')
def family_lectures(self, request):
   
pass

I can’t find out however how to get the routed urls in to the main menu.
Is it possible or not?

Cheers,

Robert




Brett Grace

unread,
Jun 13, 2016, 5:46:03 PM6/13/16
to Wagtail support
I don't think there is a generic way to do this. Let's look at the code:

https://github.com/torchbox/wagtail/blob/master/wagtail/contrib/wagtailroutablepage/models.py#L13


def route(pattern, name=None):
   
def decorator(view_func):
       
global _creation_counter
        _creation_counter
+= 1

        # Make sure page has _routablepage_routes attribute
        if not hasattr(view_func, '_routablepage_routes'):
            view_func
._routablepage_routes = []

       
# Add new route to view
        view_func._routablepage_routes.append((
            url
(pattern, view_func, name=(name or view_func.__name__)),
            _creation_counter,
        ))

       
return view_func

   
return decorator


class RoutablePageMixin(object):
   
"""
    This class can be mixed in to a Page model, allowing extra routes to be
    added to it.
    """
    #: Set this to a tuple of ``django.conf.urls.url`` objects.
    subpage_urls = None

    @classmethod
    def get_subpage_urls(cls):
        routes
= []
       
for attr in dir(cls):
            val
= getattr(cls, attr)
           
if hasattr(val, '_routablepage_routes'):
                routes
.extend(val._routablepage_routes)

       
return tuple([
            route[0]
           
for route in sorted(routes, key=lambda route: route[1])
       
])

It looks like the route annotation takes an optional name parameter, and each time the annotation is invoked it registers the route in the private _routeablepages_route array. This is used in get_subpage_urls, but it only returns the URL and throws out the name.

I don't see any way here to get the information you want, so I think you'll need to extend RoutablePageMixin to support your use case.




 

Robert Slotboom

unread,
Jun 14, 2016, 4:51:32 AM6/14/16
to Wagtail support
Hi Brett,

Hmmmm interesting. Playing around a bit I got this working.

class ProductIndexPage(RoutablePageMixin, AbstractBasePage):

   
def get_urls(self, routes):
        urls
= []
        mapping
= [('$', ''), ('^', ''),]
       
for route in routes:
            pattern
= route.regex.pattern
           
for k, v in mapping:
                pattern
= pattern.replace(k, v)
            urls
.append(pattern)
       
return urls


    def __init__(self, *args, **kwargs):
        super(AbstractBasePage, self).__init__(*args, **kwargs)
        routes = list(self.get_subpage_urls())
        self.urls = self.get_urls(routes)


Now I am able to get the route urls in the menu template.

But at a price!

I have to use .specific() in the template tag.
And I have to get the structure right.

So maybe it is a lot simpler to just add a child_menu dict to the page model, including urls, captions etc.


Cheers,

Robert





 

Brett Grace

unread,
Jun 14, 2016, 10:51:49 AM6/14/16
to Wagtail support
That's probably what I would do.

Robert Slotboom

unread,
Jun 15, 2016, 8:44:32 AM6/15/16
to Wagtail support
Hi Brett,

I managed to rewrite an existing url pattern to routes and a get_submenu method.


class ProductPage(RoutablePageMixin, AbstractBasePage):
   
    current_lang
= get_language()
 
   
@classmethod
   
def get_submenu(cls):
        menu
= []
        product_classes
= [Lecture, Course, Tour]
       
for product in product_classes:
            title
= product_name_mapping[cls.current_lang].get(product._meta.verbose_name_plural)
            menu
.append(
               
{'title': title, 'url': title + '/'}
           
)
       
for key, val in TargetGroup.CHOICES:
            title
= 'voor '+ val
            url
= 'voor-{0}/'.format(val)
            item
= {
               
'title': title,
               
'url': url,
               
'children': []
           
}
           
for product in product_classes:
                product_title
= product_name_mapping[cls.current_lang].get(product._meta.verbose_name_plural)
                child_url
= '{0}/{1}/'.format(url, product_title)
                item
['children'].append(
                   
{'title': title, 'url': title + '/'}
               
)
            menu
.append(item)
       
return menu


    @route(r'^$')
    def about(self, request):
        self.current_url = self.get_url('about')
        context = self.get_context(request)
        return TemplateResponse(
            request,
            self.get_template(request),
            context
        )

    @route(r'^voor-(?P<target_slug>[-\w]+)/$')
    def target_groups(self, request, target_slug):
        target_key = TargetGroup.get_key_for_name(target_slug)
        if target_key:
            url = self.get_redirect_url(
                'products_for_target_group', 
                kwargs = {
                    'target_slug': target_slug,
                    'product_class_slug': 'lezingen'
                }
            )
            return redirect(url)

    @route(r'^voor-(?P<target_slug>[-\w]+)/(?P<product_class_slug>[-\w]+)/$')
    def products_for_target_group(self, request, target_slug, product_class_slug):
        target_key = TargetGroup.get_key_for_name(target_slug)
        product_class = product_mapping[self.current_lang].get(product_class_slug, None)
        if target_key and product_class:
            self.current_url = self.get_url(
                'products_for_target_group', 
                kwargs = {
                    'target_slug': target_slug,
                    'product_class_slug': product_class_slug
                 }
             )
             context = self.get_context(request)
             context.update({
                 'item_list': self.get_products(product_class, target_key)
             })
             return TemplateResponse(
                 request,
                 self.get_template(request),
                 context
             )


    @route(r'^(?P<product_class_slug>[-\w]+)/$')
    def products(self, request, product_class_slug):
        product_class = product_mapping[self.current_lang].get(product_class_slug, None)
        if product_class:
            self.current_url = self.get_url(
                'products',
                kwargs = {
                    'product_class_slug': product_class_slug
                }
            )
            context = self.get_context(request)
            context.update({
               'item_list': self.get_products(product_class, product_class_slug)
            })
            return TemplateResponse(
                request,
                self.get_template(request),
                context
            )


Then I changed my template tag like this:

from django import template
register = template.Library()

@register.inclusion_tag('cms/tags/main_menu.html', takes_context=True)
def main_menu(context, calling_page=None):
    site
= context.get('current_site')
    home_page
= site.root_page
    menuitems
= home_page.get_children().specific().live().in_menu()
   
for menuitem in menuitems:
        menuitem
.active = calling_page.url.startswith(menuitem.url) if calling_page else False
        menuitem
.children = menuitem.get_children().specific().live().in_menu()
        
if hasattr(menuitem, 'get_submenu'):
            menuitem
.submenu = menuitem.get_submenu()
 
    return {
        'home_page': home_page,
        'calling_page': calling_page,
        'menuitems': menuitems,
        'request': context['request'],
   
}


And the result is...




Wagtail is really nice and I’m almost convinced it will replace both DjangoCMS and FineCMS as my system of choice.


One thing left: determine if the menu item is active.
Is there any way to get the parent of the curent route/view?
No non, not the calling page....

/op-verzoek/
/op-verzoek/voor-particulieren/    (this one)
/op-verzoek/voor-particulieren/lezingen/


Cheers,

Robert
 


 

Robert Slotboom

unread,
Jun 15, 2016, 10:53:23 AM6/15/16
to Wagtail support
Hi Brett,

Got it:


class ProductPage(RoutablePageMixin, AbstractBasePage):
   
    def get_route_items(self):
        page_url = self.url 
        route_items = []
        product_classes = [Lecture, Course, Tour]
        for product in product_classes:
            title = product_name_mapping[self.current_lang].get(product._meta.verbose_name_plural)
            route_items.append(
                {'title': title, 'url': '{0}{1}/'.format(page_url, title)}
            )
        ...

    def
get_parent_urls(self):
        parent_urls
= []
        
if self.current_url:
            url_parts
= list(filter(None, self.current_url.split('/')))
            parent
= '/{0}/'.format(url_parts.pop(0)) if url_parts else None
            current
= url_parts.pop() if url_parts else None
            
if url_parts:
                
for part in url_parts:
                    parent
= '{0}{1}/'.format(parent, part)
                    parent_urls
.append(parent)
           
return parent_urls

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request)
        context.update({
            'current_url': self.current_url,
            'parent_urls': self.get_parent_urls()
        })

    @route(r'^$')
    def about(self, request):
        self.current_url = self.get_url('about')
        ...



templatetag

...
for
menuitem in menuitems:

    menuitem
.active = calling_page.url.startswith(menuitem.url) if calling_page else False
    menuitem
.children = menuitem.get_children().specific().live().in_menu()

    
if hasattr(menuitem, 'get_route_items'):
        menuitem
.route_items = menuitem.get_route_items()
 
    
return {
        ...
        
'parent_urls': parent_urls,
        
'current_url': current_url,
 
                ...
    }

template

{% for item in menuitem.route_items %}
    <li{% if item.url in parent_urls or item.url == current_url %} class='active'{% endif %}><a href="{{ item.url }}">{{ item.title }}</a></li>
{% endfor %}


Cheers,

Robert

Reply all
Reply to author
Forward
0 new messages