0

I need to invoke Django's reverse() method to construct a URL for a certain view function style_specific. Its entry in urls.py is below:

Figure 1

url(r'^(?P<jewelry_type>[A-Za-z0-9]+)/(?P<jewelry_style_user>[A-Za-z0-9]+)/(?P<jewelry_id>[0-9]+)/$',views.style_specific,name="style_specific"),

I'm also using Algolia, (a search engine) that plugs its own parameters into the constructed URLS, so I need to use reverse() to insert placeholders that can then be manipulated by Algolia. This previously worked when I called reverse twice --

Figure 2

specific_url = (reverse('jewelry:specific_method',args=[model_name,177013,1337]).replace('1337','{{objectID}}')).replace('177013','{{jewelry_style}}')

where model_name is a parameter passed into the view function constructing style_specific's URLs, and 177013 and 1337 are placeholders that get replaced eventually. However, this approach is a hackish one that will get hard to maintain later. To that end, I wanted to replace this solution with something more flexible, along the lines of this SO answer. Ideally, the placeholders/replacements would be determined by a dictionary replace_parameters defined like

Figure 3

replace_parameters={'placeholder_1':'replacement_1','placeholder_2':replcement_2} #and so on

Unfortunately this is where my problem comes in. In Figure 2, the parameters is passed into args a hard-coded list. (Or at least that's what the syntax appears as) The solution I attempted to overcome this obstacle was to call replace_parameters's keys as a list -- that is, list(replace_parameters.keys()) par this SO answer. (See below)

Figure 4

specific_url = reverse('jewelry:specific_method',args=list(replace_parameters.keys()))

This approach yielded this NoReverseMatchspecifically:

Figure 5

Reverse for 'style_specific' with arguments '('jewelry_type', 'jewelry_style_user', 'jewelry_id')' not found. 1 pattern(s) tried: ['jewelry/(?P<jewelry_type>[A-Za-z0-9]+)/(?P<jewelry_style_user>[A-Za-z0-9]+)/(?P<jewelry_id>[0-9]+)/$']

where jewelry_type,jewelry_style_user and jewelry_id are the keys from replace_parameters.

As part of my debugging, I double-checked Figure 1's regex and verified it passed using a different view function. I also verified list(replace-parameters.keys()) by testing isinstance(list(replace_parameters.keys()),list). I also tried placing brackets around list(replace_parameters.keys()) but that did nothing either. Django's documentation on reverse() implies args should have a list passed into it, but this is not made clear. Thus my question is: does reverse() allow a list object to be passed into args or must it be hard-coded?

Brian Lee
  • 41
  • 5
  • Pleas add what `replace_parameters` contains for real (not "placeholders"). And full error message. If this is possible, please make your question shorter. – Ivan Starostin Aug 14 '18 at 20:50
  • `replace_parameters` specifically is `{'jewelry_type':obj.__name__,'jewelry_style_user':'{{ jewelry_style}}','jewelry_id':'{{objectID}}'})` where `obj` is a Django model class. `{{objectID}}` and `{{jewelry_style}}` eventually get overridden by Algolia. – Brian Lee Aug 14 '18 at 20:55
  • With `kwargs` attempt error message should differ. What is it? – Ivan Starostin Aug 14 '18 at 20:58
  • `Reverse for 'style_specific' with keyword arguments '{'jewelry_type': 'Metal', 'jewelry_style_user': '{{ jewelry_style }}', 'jewelry_id': '{{objectID}}'}' not found. 1 pattern(s) tried: ['jewelry/(?P[A-Za-z0-9_-]+)/(?P[A-Za-z0-9_-]+)/(?P[0-9_-]+)/$']` – Brian Lee Aug 14 '18 at 21:03
  • Some one must evaluate `'jewelry_style_user': '{{ jewelry_style }}', 'jewelry_id': '{{objectID}}'` placeholders within {{}}. `Reverse` function can see only `{{ jewelry_style }}` as string, as it is, which truly does not match `[A-Za-z0-9_-]+` pattern. `kwargs` is the right way. – Ivan Starostin Aug 14 '18 at 21:06
  • In that case, should I modify my regex to allow `{` and `}`? – Brian Lee Aug 14 '18 at 21:54
  • Django will not compute those '{{ adsf }}'. Such placeholders are for templates. Outside of templates they don't work. – Ivan Starostin Aug 15 '18 at 07:05

1 Answers1

0

replace_parameters.keys() means keys, not values.

Why don't you use kwargs which are more specific for named placeholders matching? Especially when replace_parameters is already a dict.

specific_url = reverse('jewelry:specific_method', kwargs=replace_parameters)

And someone must evaluate 'jewelry_style_user': '{{ jewelry_style }}', 'jewelry_id': '{{objectID}}' placeholders within {{}}. Reverse function can see only {{ jewelry_style }} as string, as it is, which truly does not match [A-Za-z0-9_-]+ pattern.

Ivan Starostin
  • 8,798
  • 5
  • 21
  • 39
  • I tried modifying my regex into something that might work (e.g. `(?P[A-Za-z0-9_{}]+)`) but nothing came out of that. – Brian Lee Aug 15 '18 at 16:30
  • This is not regex problem. Regex is fine. Fix values in your dict. Replace `'{{ jewelry_style }}'` with real value. – Ivan Starostin Aug 15 '18 at 18:00
  • The issue is that Algolia checks for `{{ jewelry_style }}` to put in the real values -- i.e. `reverse()` doesn't directly insert the real values. A workaround I was considering is to simply hardcode the urls, but `reverse()` exists for a reason. – Brian Lee Aug 15 '18 at 19:16
  • As you stated in the original question: `does reverse support list/dict arguments` - yes it does. Passing your `replace_parameters` as `kwargs` is just fine. `Reverse` does support this parameter and understands what to do. The problem is inside `replace_parameters` - it contains placeholders instead of values. Which do not match your regex. That's it. – Ivan Starostin Aug 15 '18 at 19:36