I'm having some trouble sending along more than one variable to the view.
my urls.py is as follows:
urlpatterns = patterns('',
url(r'^rss/(?P<anything>[^/]+)/$', 'rss.rssama.views.makerss', name='anything'),
url(r'^$', 'rss.rssama.views.home'),
)
views.py
def maakrss(request, anything):
So now it takes from www.mydomain.com/rss/[anything]/ and sends 'anything' to my view. However I also want it to send along another string to views.py, like:
www.mydomain.com/rss/[anynumber]/[anystring]/
I tried this but that didn't work:
url(r'^rss/(?P<anynumber>[^/]+)/(?P<anystring>[^/]+)/$', 'rss.rssama.views.makerss', name='anynumber', name2='anystring'),
But this doesn't work, it gives this error: keyword argument repeated (urls.py, line 17)
So my question: How can I make it to give along two string from the url?
To begin with, the regex part should look like this:
r'^/rss/(?P<anynumber>\d+)/(?P<anystring>.+)/$'
Those strings inside the <...> parts allow you to give a name to whatever the regex matches. Django will then use that name to pass the value to your function. Therefore your function must have an argument with the same name. In this case, Django will take the value called anynumber and use that value for the parameter of your function that is called anynumber. The same goes for anystring, and this system frees you from worrying about what order the arguments of your function are in.
\d+ will match one or more numeric characters (digits). It's good practice to limit the regex to match only numbers if that's what you intend to catch, rather than any character and hope that only numbers appear. If you wanted to limit the digits part to a certain number of digits, you could use \d{1,4} to take from one to four digits.
The next part, (?P<anystring>.+) will catch a string consisting of one or more of any characters. This would actually match something like 'letters/moreletters', including the slash. There are a number of "special sequences" in Python regex that might help. To match only digits, letters, and the underscore character, use \w, as in (?P<anystring>\w+). To be more lax but ignore whitespace or any other non-sense, (?P<anystring>[a-zA-Z1-9:;_{}\[\]] to catch a whole slew of characters. Make sure to escape anything that might be a special character in a regex. However, be conservative. If you allow too many options who knows what sorts of bugs you'll have to work out later.
Now onto name parameter of the url function. That name is not what it will pass the caught patterns to your functions as. It's a name for a particular class of invocation of your view function that can be used as a short-hand in other contexts like, the template tag {% url view-name arg1 arg2 %}. So, the name you have already, "anything", refers to a call to your view function, passing it one keyword argument that happens to be called anything. For the case where you want to pass two strings, give that a name like "rss-number-string" to signify the arguments you want to take, or a name that refers to the special function your view will be performing with that combination.
I use multiple names for the same function all the time, and the key is this:
def makerss(request, anystring=None, anynumber=None):
By giving the parameters default values, it allows you to use the same function in different ways. In this case, the function can be used when you only want to pass a value for anystring, or when anystring and anynumber should have values.
I know this is a lot of different points, so I'll try to put it all together so you can see how it might work. To have two urls, one which catch a string and passes it on, and another which catches a number, a slash, and then a string, but both point to the same view function, you could use this:
urlpatterns = patterns('',
url(r'^rss/(?P<anystring>\w+)/$', 'rss.rssama.views.makerss', name='rss-anystring'),
url(r'^rss/(?P<anynumber>\d+)/(?P<anystring>\w+)/$', 'rss.rssama.views.makerss', name='rss-number-string'),
url(r'^$', 'rss.rssama.views.home'),
)
With a view function something like this:
def makerss(request, anystring=None, anynumber=None):
if anystring:
if anynumber:
#Do something with the string and the number
else:
#Do something with just the string
Please let me know if this helps. Also, Django rocks, so kudos!
Python Regex Library Docs
You don't really need to give two name arguments for this. I mean, you already have the variable names inside regex. The actual problem is, you cannot give two name arguments, so you can do this instead:
url(r'^rss/(?P<anynumber>[^/]+)/(?P<anystring>[^/]+)/$', 'rss.rssama.views.makerss',name='something'),
EDIT:
using the urlConf above you can create corresponding view as:
def makerss(request, anynumber, anystring):
What is name2 supposed to be? The url function takes a name parameter, which is the name of the URL when you reverse it, but you can't put random extra functions.
Otherwise, you have the right syntax for sending two elements to a view. Of course, since you've masked the variable names and not provided the actual error or traceback, we have no way of knowing what really is going wrong.
Related
When one supplies URL args or kwargs to a django.urls.reverse() call,
Django will nicely URL-encode non-Ascii characters and URL-reserved characters.
For instance, given a declaration such as
path("prefix/<stuff>", view=MyView.as_view(), name="myurl")
we get
reverse('myurl', args=['aaa bbb']) == "/prefix/aaa%20bbb"
reverse('myurl', args=['aaa%bbb']) == "/prefix/aaa%25bbb"
reverse('myurl', args=['Ä']) == "/prefix/%C3%84"
and so on. So far, so good.
What Django will not encode, however, is the slash:
reverse('myurl', args=['aaa/bbb'])
will give us
django.urls.exceptions.NoReverseMatch:
Reverse for 'myurl' with arguments '('aaa/bbb',)' not found.
1 pattern(s) tried: ['prefix/(?P<stuff>[^/]+)$']
(The question what to encode and what not
has been discussed as a Django issue.
It's complicated.)
I found a remark in the code that may explain why
the slash is a special case:
_reverse_with_prefix in django/urls/resolvers.py contains a comment that says
# WSGI provides decoded URLs, without %xx escapes, and the URL
# resolver operates on such URLs. First substitute arguments
# without quoting to build a decoded URL and look for a match.
# Then, if we have a match, redo the substitution with quoted
# arguments in order to return a properly encoded URL.
Given that unencoded arguments are used in the matching initially,
it is no wonder that it does not work:
The slash looks like the end of the argument to Django and so there is
one more argument than expected.
My question:
I dearly want to use user-supplied data in natural-looking URLs,
so slashes occur occasionally. How can I make them work?
The URL structure I need is basically
/show_rooms/<organization>/<department>/<building>
I can think of these approaches:
Replace a slash in an argument with some exotic Unicode character
that will never occur otherwise. And back for received arguments.
This would sort of do the job, but is inconvenient,
non-standard, and therefore ugly.
Use slugs instead of the real names.
This would require extending my models to store the slugs (because
the ORM needs to find objects by them) and appears out of proportion to me.
URL-quote my arguments before passing them to reverse()
and unquote arguments when I receive them.
This is as inconvenient as (1).
It leads to URLs that are more difficult to read than
those from (1), because each % produced by quoting
will subsequently be encoded as %25.
But at least it is a standard-ish approach.
Sigh. Is this really the "right" way?
Any comments or fourth solutions are welcome!
Now that I've written it up, solution (1) does not look quite
so horrible to me. What replacement character would you use for a slash?
I suggest you try to pass the slash in as a regular URL and see if your view is able to match with it. If that's the case and the problem is in the reverse function itself not the view. How about passing the slash already encoded %2F?
It's me, the asker.
Here is the solution that I finally used:
I decided for solution (1).
It turned out less inconvenient than I had expected and
it works spectacularly well.
My Firefox browser shows the URL in text form, not urlencoded form,
and when you pick the right replacement character it looks almost natural.
Very nice.
Here is the code for the escaping (to be called in the template, hence a
custom templatetag)
and unescaping (to be called in the view):
import django.template as djt
register = djt.Library()
# see https://docs.djangoproject.com/en/stable/howto/custom-template-tags/
ALT_SLASH = '\N{DIVISION SLASH}'
#register.filter
def escape_slash(urlparam: str) -> str:
"""
Avoid having a slash in the urlparam URL part,
because it would not get URL-encoded.
See https://stackoverflow.com/questions/67849991/django-urls-reverse-url-encoding-the-slash
Possible replacement characters are
codepoint char utf8 name oldname
U+2044 ⁄ e2 81 84 FRACTION SLASH
U+2215 ∕ e2 88 95 DIVISION SLASH
U+FF0F / ef bc 8f FULLWIDTH SOLIDUS FULLWIDTH SLASH
None of them will look quite right if the browser shows the char rather than
the %-escape in the address line, but DIVISION SLASH comes close.
The normal slash is
U+002F / 2f SOLIDUS SLASH
To get back the urlparam after calling escape_slash,
the URL will be formed (via {% url ... } or reverse()) and URL-encoded,
sent to the browser,
received by Django in a request, URL-unencoded, split,
its param parts handed to a view as args or kwargs,
and finally unescape_slash will be called by the view.
"""
return urlparam.replace('/', ALT_SLASH)
def unescape_slash(urlparam_q: str) -> str:
return urlparam_q.replace(ALT_SLASH, '/')
I have a variable whose value is say FileSystem, I would like to get this printed as file_system in my template. Observe I am doing 2 things here:
1. If the first letter of my string is capital letter then I am converting it to lower
2. If any of my other letters is in caps, then I am replacing it with underscore followed by its lower string format.
One more example would be converting StackOverFlow to stack_over_flow
How can I get this done?
You could write a custom jinja filter that takes your variable as input and allows you to apply your desired transformation.
Quoted from the official jinja help:
Custom filters are just regular Python functions that take the left side of the filter as first argument and the arguments passed to the filter as extra arguments or keyword arguments.
jinja help section for custom filters
Example:
def convert_to_snakecase(value):
# convert your value here (lower case first letter + snake case)
return formatted_value
Register your custom filter:
app.jinja_env.filters['convert_to_snakecase'] = convert_to_snakecase
Call your filter inside the template:
{{ my_variable|convert_to_snakecase }}
I have a route:
#app.route("/login/<user>/<timestamp>")
def user(user, timestamp):.
But, I need it in this form -
#app.route("/login/<user><timestamp>")
def user(user, timestamp):.
i.e without the slash('/').
Is there any way to do it ?
Short answer: It is possible given the two parameters have a non-overlapping pattern. By giving it a wildcard-pattern however (you did not specify the converter). It will result in the fact that all content is handled to the user. That being said, it is advisable to have a clear separator.
As is specified in the documentation, you can define variables by writing them like HTML tags, like <var>, you can also specify a converter, like <converter:var>. If you do not specify a converter, the parameter is assumed to be a string that can not contain slashes.
There are however other converters, like int, float, path and uuid.
If the patterns are written in such way that it is clear when the first pattern ends, and the second pattern begins, then it this can be handled. For example:
#app.route("/login/<int:day><user>")
can work, given user can not start with a digit, since here once the sequence of digits ends, Flask will parse the <user> parameter.
By writing #app.route("/login/<user><timestamp>") however, the two patterns are overlapping: if we do not have a parsing strategy any split could be a valid one. Since the engine is greedy if I recall correctly, in practice it will result in the fact that user takes all characters, and timestamp none.
Since the default string does not include a slash, we know that the slash acts as a clear separator, since it is not included in both variables in your example.
I am writing a web crawler using Scrapy and as a result I get a set of URLs like: [Dummy URLs]
*http://matrix.com/en/Zion
http://matrix.com/en/Machine_World
http://matrix.com/en/Matrix:Banner_guidelines
http://matrix.com/en/File:Link_Banner.jpg
http://matrix.com/wiki/en/index.php*
In the rules in scrapy, I want to add a regex that allows urls ONLY of the kind "http://matrix.com/en/Machine_World" or "http://matrix.com/en/Zion"
i.e urls that contain anything outside of the set "http://matrix.com/en/<[a-zA-Z,_]>" must not be allowed.
Constraints :
The string after "/en/" could be of any length. So I cannot ask it to look only for the first 10 or 20 characters. e.g when I use the regex : [a-zA-Z,]{1,20} OR [a-zA-Z,]{1,} it still matches the URLs like http://matrix.com/en/Matrix:Banner_guidelines coz it finds "http://matrix.com/en/Matrix" part of the url a successful match. I want it look at the string starting after "/en/" till the end of URL and then apply this rule.
Unfortunately I cannot extract that string n write a sub-routine of any kind. It has to be done using a regex only!
i.e urls that contain anything outside of the set "http://matrix.com/en/<[a-zA-Z,_]>" must not be allowed.
Have you tried using this character class in your regex? Looks like you aren't including underscores.
Try
[a-zA-Z,_]+
The plus sign means "one or more" - which is the same as {1,} just a nice shorthand :)
If you want to exclude items with .php or .jpg, feel free to add a $ sign to the end, as so:
[a-zA-Z,_]+$
The $ means "end of line" meaning that your matching sequence must run to the end of the line. As fullstops are not included in the character class, those options will be excluded
Let me know if that works,
Elliott
Reproducible evidence that the suggested regex works:
grep("matrix.com\\/en\\/[a-zA-Z,_]+$", x, perl=TRUE, value=TRUE)
#[1] "http://matrix.com/en/Zion"
#[2] "http://matrix.com/en/Machine_World"
Data
x <- c("http://matrix.com/en/Zion", "http://matrix.com/en/Machine_World",
"http://matrix.com/en/Matrix:Banner_guidelines",
"http://matrix.com/en/File:Link_Banner.jpg",
"http://matrix.com/wiki/en/index.php")
I need to add a <br/> after each character in a string which I'm passing into the template. While one way of doing it would involve doing it before it is passed into the template (i.e. in the Python code), I'm wondering if it is possible to do it in the actual template.
Or would I be better off creating a helper function and invoking it from the template, and if so, how would I import that function/module into the template.
The one thing I want to avoid is mixing HTML bits with the view code so adding the <br/>s in the view should only be the last resort.
Strings in Python are sequences, therefore the individual characters in them can be joined.
How about you make such a function in the javascript, and after passing the string to the template, instead of using it directly, you can call that function on the string, and the function returns the string with every character is followed by a <br/>?
var nStr = "";
for (i=0;i<str.length;i++)
{
nStr = nStr.concat(str.charAt(i),"<br/>");
}