Recursive URL Patterns CMS Style - django

Whenever I learn a new language/framework, I always make a content management system...
I'm learning Python & Django and I'm stuck with making a URL pattern that will pick the right page.
For example, for a single-level URL pattern, I have:
url(r'^(?P<segment>[-\w]+)/$', views.page_by_slug, name='pg_slug'),
Which works great for urls like:
http://localhost:8000/page/
Now, I'm not sure if I can get Django's URL system to bring back a list of slugs ala:
http://localhost:8000/parent/child/grandchild/
would return parent, child, grandchild.
So is this something that Django does already? Or do I modify my original URL pattern to allow slashes and extract the URL data there?
Thanks for the help in advance.

That's because your regular expression does not allow middle '/' characters. Recursive definition of url segments pattern may be possible, but anyway it would be passed as a chunk to your view function.
Try this
url(r'^(?P<segments>[-/\w]+)/$', views.page_by_slug, name='pg_slug'),
and split segments argument passed to page_by_slug() by '/', then you will get ['parent', 'child', 'grandchild']. I'm not sure how you've organized the page model, but if it is not much sophiscated, consider using or improving flatpages package that is already included in Django.
Note that if you have other kind of urls that does not indicate user-generated pages but system's own pages, you should put them before the pattern you listed because Django's url matching mechanism follows the given order.

Related

Hierarchical URLs in Django

Is there a way to implement hierarchical query pattern in Django? As far as I know, the framework only allows to route to views by parsing URLs of a specific format, like:
/customers/{order} -> customer.views.show_orders(order)
But what if I need something like this:
/book1/chapter1/section1/paragraph1/note5 -> notes.view.show(note_id)
where note_id is the id of the last part of the URL, but the URL could have different number of components:
/book1/chapter1
/book1/chapter1/section1
etc.
Each time, it would point to the relevant part of the book depth depending on the depth. Is this doable?
I know there is this: https://github.com/MrKesn/django-mptt-urls, but I am wondering if there is another solution. This isn't ideal for me.
Django URLs are just regular expressions, so the simplest way would be to just ignore everything prior to the "note" section of the URL. For example:
url(r'^.*/note(?P<note_id>[0-9]+)$', 'notes.view.show'),
However, this would ignore the book, chapter, paragraph components. Which would mean your notes would need unique ids across the system, not just within the book. If you needed to capture any number of the interim parts it would be more complicated.
I can't confirm this will work right now, but using non-capture groups in regular expressions, you should be able to capture an optional book and chapter like so:
url(r'^(?:book(?P<book_id>[0-9]+)/)?(?:chapter(?P<chapter_id>[0-9]+)/)?note(?P<note_id>[0-9]+)$', 'notes.view.show'),
Use named groups to accomplish this: https://docs.djangoproject.com/en/dev/topics/http/urls/#named-groups
url(r'^book(?P<book_id>\d+)/chapter(?P<chapter_id>\d+)/section(?P<section_id>\d+)/paragraph(?P<paragraph_id>\d+)/note(?P<note_id>\d+)$', notes.view.show(book_id, chapter_id, section_id, paragraph_id, note_id)
For those who really need a variable-depth URL structure and need the URL to consist strictly of slugs, not IDs, knowing all the components of the URL is critical to retrieve the correct record from the database. Then, the only solution I can think of is using:
url(r'^.*/$', notes.views.show, name='show')
and then parsing the content of the URL to get the individual components after retrieving the URL in the view using the request.path call. This doesn't sound ideal, but it is a way to accomplish it.

Django documentation, part3 understanding problems

I read the documentation of Django but now I am at a point where I need some explanation. It is on this site and I understand the views but I really don't get how the urls work. It looks pretty cryptic and confusing to me. Can anybody explain to me how the urls work and what their purpose is?
Your urls.py file is virtual. They do it this way so you don't need to worry about a static url to http://yoursite.com/polls/34. By using this number as a regular expression /(d+) you can keep it dynamic so one url with this regular expression can be millions of different polls.
when the url is requested that regular expression number (whether it's 1 or 13352) is sent to the view which then says, I need to query the database for a Poll that has a PrimaryKey (PK) of whatever this number is. If it's found the Poll object is sent to the template by the view. The template then displays all the data in the poll object.
The bottom line is using something like this you can have one line for a url which is essentially millions of different urls. I use this same format for a movies website I'm creating www.noobmovies.com. I follow the same structure for Stars, Movies and blogs. Essentially three lines of code has created urls for 10,000 pages or so.
There is a dedicated Django documentation page for that: https://docs.djangoproject.com/en/1.6/topics/http/urls/
Maybe it will help you?

Django urls having N number of optional parameters

I want to have same url pattern as khanacademy.org have in its videos. Suppose I opened a video having title "The Beauty of Algebra" its url will be http://www.khanacademy.org/math/algebra/introduction-to-algebra/v/the-beauty-of-algebra.
It shows three level of categories (math, algebra and introduction to algebra) in url. The same I want in django. Level of categories(no. of parameters) may increase up to N.
I didn't want to hard code the Urls like
url(r"^(?P<level_one>[a-zA-Z]+)/v/(?P<slug>[-\w]+)/$", "my_view", name="level_one"),
url(r"^(?P<level_one>[a-zA-Z]+)/(?P<level_two>[a-zA-Z]+)/v/(?P<slug>[-\w]+)/$", "my_view", name="level_two"),
url(r"^(?P<level_one>[a-zA-Z]+)/(?P<level_two>[a-zA-Z]+)/(?P<level_three>[a-zA-Z]+)/v/(?P<slug>[-\w]+)/$", "my_view", name="level_three"),
...
Thanks!
I don't believe it is possible to have a variable number of parameters in your urls.py. The best option may be to have a single generic catch-all url that points to a view where you write your own url dispatcher which is more complicated than what django's urls can provide.
You'd be able to parse the path and handle N number of levels within your view.

Django catch-all URL without breaking APPEND_SLASH

I have an entry in my urls.py that acts as a catch-all which loads a simple view if it finds an appropriate page in the database. The problem with this approach is that the URL solver will then never fail, meaning that the APPEND_SLASH functionality won't kick in - which I need.
I'd rather not have to resort to adding a prefix to the static page URLs to stop it being a catch-all. I do know about flatpages, which uses a 404 hook rather than an entry in urls.py, and I had kinda hoped to avoid having to use it, but I guess this problem might be exactly the kind of reason why one would use it.
Any way round this problem or should I just give in and use flatpages?
Make sure that your catch-all URL pattern has a slash at the end, and that the pattern is the last in your URLconf. If the catch-all pattern doesn't end with a slash, then it will match stray URLs before the middleware tries appending a slash.
For example, use r'^.*/$' instead of r'^.*' as your last pattern.
To do the same, but pass the url to the view as a named argument, use r'^(?P<url>.*)/$'.
The statement if it finds an appropriate static page in the database seems like your static pages are not quite static so, you either pass your links through urls.py (just like you do now), or you extract those pages from the DB, put them in a directory and configure that directory as one for serving static files

Match all characters in group except for first and last occurrence

Say I request
parent/child/child/page-name
in my browser. I want to extract the parent, children as well as page name. Here are the regular expressions I am currently using. There should be no limit as to how many children there are in the url request. For the time being, the page name will always be at the end and never be omitted.
^([\w-]{1,}){1} -> Match parent (returns 'parent')
(/(?:(?!/).)*[a-z]){1,}/ -> Match children (returns /child/child/)
[\w-]{1,}(?!.*[\w-]{1,}) -> Match page name (returns 'page-name')
The more I play with this, the more I feel how clunky this solution is. This is for a small CMS I am developing in ASP Classic (:(). It is sort of like the MVC routing paths. But instead of calling controllers and functions based on the URL request. I would be travelling down the hierarchy and finding the appropriate page in the database. The database is using the nested set model and is linked by a unique page name for each child.
I have tried using the split function to split with a / delimiter however I found I was nested so many split statements together it became very unreadable.
All said, I need an efficient way to parse out the parent, children as well as page name from a string. Could someone please provide an alternative solution?
To be honest, I'm not even sure if a regular expression is the best solution to my problem.
Thank you.
You could try using:
^([\w-]+)(/.*/)([\w-]+)$
And then access the three matching groups created using Match.SubMatches. See here for more details.
EDIT
Actually, assuming that you know that [\w-] is all that is used in the names of the parts, you can use ^([\w-]+)(.*)([\w-]+)$ instead and it will handle the no-child case fine by itself as well.