django NoReverseMatch error for related object when in a loop - django

My model: a "Workshop" has a related item that stores time and place in a "Session" object:
item=models.OneToOneField(Session)
a Session stores the location as a foreign key to the locations:
location = models.ForeignKey(conference_models.Location,
verbose_name=_("location"), blank=True, null=True)
I'm showing a Workshop in a template and I want to link to its location. If the template is a single Workshop view, this all works, but if I pass a list of workshops and wrap this all in:
{% for w in workshops %}
then bad things happen...
I have this in urls.py to define the URL for a location view:
url(r'^locations/(?P<location_pk>\d+)/$',
views.view_location, name='view-location'),
and in the workshop template I have:
<a href="{% url 'view-location' location_pk=w.item.location.pk %}">
{{w.item.location.pk}} {{w.item.location}}</a>
- {{w.item.start}} to {{w.item.end}}
And I get Reverse for 'view-location' with arguments '()' and keyword arguments '{'location_pk': ''}' not found.
where location_pk is the parameter to my location view. Everything seems to be correct. For example, if I do:
<a href="{% url 'view-location' location_pk=123546 %}">
{{w.item.location.pk}} {{w.item.location}}</a>
- {{w.item.start}} to {{w.item.end}}
I get the expected URL with 123546 in it. If I do:
<a href="{% url 'view-location' location_pk=w.item.pk %}">
{{w.item.location.pk}} {{w.item.location}}</a>
- {{w.item.start}} to {{w.item.end}}
then I get the item primary key in the URL (which isn't what I want, but proves the point that I'm not going mad expecting this to work...).
In all cases the {{w.item.location.pk}} tag expands to the correct value.
I've tried wrapping it in a {% with %} tag so there's no dotting going on. No joy.
This is Django 1.4.5, part of a complex project that probably won't handle an update to 1.5. If this requires 1.5 for a bigfix I'll have to rethink...
Note this only seems to happen in a {% for %} loop...

Since location field has null=True, blank=True, There might be some entries which are null, hence w.item.location.pk is evaluating to ''.
You can check {% if w.item.location %} and then load the URL for location.

Related

How to use loop for requesting from html template in Django

From html template say "createlist" I want to add link which actually request server to get one more object of image form to the "createlist" page. But when I run the code it actually happens only one time but I want maximum of 5 times this should be accepted. So here how I can use loop in html template so that it will get the request again and again up to the limit say 5.
here is the template code -
{% if morepicform %}
{{ morepicform }}
{% endif %}
Want to add more pics? <a href="{% url 'createlist' 'morepic' %}" role="button">
<img src="static/auctions/plus-circle-solid.svg" height="20px" width="20px"></a>
views function
def morepic(request, morepic=''):
return render(request, "auctions/createlist.html",{
"morepicform" : PictureForm(),
"picform" : PictureForm(),
"listform" : ListingForm()
})
url pattern path function -
path("createlist/<str:morepic>", views.morepic, name="createlist")
Now how can I add the functionality of requesting "morepicform"?

Could not parse the remainder: '/{{menu.Info.page}}' from ''item'/{{menu.Info.Page}}'

<img id="page" class="abc" src="{{STATIC_URL}}page/code_251.png" style=""/>
Hitting this url like localhost:8000/app/page works fine.
If I want something from views and append that pageid with url then showing error as Couldn't parse.
From views:{{page.pageid}}
output url should be :localhost:8000/app/?pageid=xx
for this i tried with below syntax:
<img id="page" class="abc" src="{{STATIC_URL}}page/code_251.png" style=""/>
But above syntax did't worked for me.
urls.py
url(r'^(page)(?:/(?P<page_id>[0-9]+))?/$',page_ViewDetails_TemplateView.as_view(),name="page"),
May be some changes need to be done on urls.py as well.
Can someone share some idea!!
You're confused about at least two things here.
Firstly, you would never use {{ }} inside a tag. You're already in the template language context there: you have access to variables directly.
Secondly, the {% url %} tag works on urlpattern names and parameters, not literal URLs. And your page URL does not expect a querystring value for page_id: it expects it as part of the path. Your generated URL needs to be "/page/3", not "/page?page_id=3".
So your URL tag is just:
<a href="{% url 'page' page_id=page.pageid %}">

Showing 'cancel' on login page to return user to where they were (using django.contrib.auth)

We are using the #login_required decorator so that users see a login page if they try to access a url for which they need to be authenticated.
We want to show a 'cancel' button on the login page, which should return the user to whichever page they were on when they tried to access the url (by clicking a link etc - we don't need to deal with them manually entering the url).
At the moment our login.html looks for a request parameter 'login_cancel_url' and if present uses that (otherwise the home page).
However, this means we have to manually pass this parameter (set to the url of the current page) whenever we show a link or button that leads to an 'authentication required' url.
Is there a more elegant way to do this?
Thanks, Martin
Well you can try get the referrer header from the request but as far as I am aware, it's browser dependent and is not very reliable so the way you are doing it is probably best. You could try make life easier by creating template tags to avoid having to rewrite the return URL manually.
You are easily able to get the current URL from django's request object on any page, so instead of setting it manually on the link, you could write a snippet of html:
link_to_login.html
<!-- You should probably get /login/ using the {% url ... %} template tag -->
<a href="/login/?login_cancel_url={{ request.path|urlencode }}">
Login Page</a>
and use the {% include "link_to_login.html"%} template tag.
Alternatively, If the text needs to be different depending on the link you can instead create an inclusion template tag:
templatetags/extra_auth_tags.py
#register.inclusion_tag('templates/extra_auth_tags/login_link.html')
def login_link(context, text=None):
return {
'text':text
}
templates/extra_auth_tags/login_link.html
<!-- You should probably get /login/ using the {% url ... %} template tag -->
<a href="/login/?login_cancel_url={{ request.path|urlencode }}">
{% if text %}
{{ text }}
{% else %}
Some Default Text
{% endif %}
</a>
and then call it in your templates as {% login_link text="Check you messages" %}. Be aware that keyword arguments for inclusion tags are only supported in the django dev version so you might need to write the template tag by hand.

Link to Current Page in Django, with Additional GET Params?

How do I place an HTML link what refers to the current page, and which adds additional GET parameters (or overwriting existing ones, if they already exist)?
Right now I have something like:
My Link
Currently, request is passed to the page. If request.path is https://stackoverflow.com/, then the resulting link becomes https://stackoverflow.com/?Key=Value
The problem?
But of course, if the current URL is https://stackoverflow.com/?PrevKey=PrevValue then it becomes:
https://stackoverflow.com/?PrevKey=PrevValue?Key=Value
Notice the incorrect second question mark -- it should in fact be:
https://stackoverflow.com/?PrevKey=PrevValue&Key=Value
Furthermore, if there is already a key with the same name, then instead of overwriting it, my current solution ignores it -- which is wrong.
How do I solve these two problems?
You'll need a custom tag. There are a couple on djangosnippets - this one looks pretty comprehensive.
For anyone seeing this in future: https://bitbucket.org/monwara/django-url-tools
You can use: {{ request.get_full_path }} to get the current path with additional parameters.
I'm just a beginner with Django, so you'll have to take my response with a grain of salt.
Firstly, request.path should return the path to the page without the domain, according to the documentation. So if the request is for http://example.com/, request.path should be just "/". Correct me if I'm wrong.
But that's not relevant. More importantly, request.path won't have any query parameters, so if the page requested is http://example.com/?PrevKey=PrevValue, then request.path will still be "/". If you want to get the query parameters, you have to make use of the dictionary-like access of the GET and POST properties (in this case GET) of the request object. Or better yet, access them through the QueryDict methods.
What I would do here, and this is by no means the best method nor code, is to prepare a custom template filter in which you pass the current request object and the key-value pair to test against.
This is how your template code would look. Note that you can still hard code the key value pair, although here it is formatted as a string with "key colon value". The filter function can handle (if you need to) more than just one set of key-value pairs.
My Link
The filter function:
from urllib import urlencode
def addQueryParameter(request, parameter):
# parse the key-value pair(s) in parameter (which looks like a JSON string)
# then add to dictionary d
# note i am not taking into account white-space.
for param in string.split(','):
key, value = param.split(':', 1)
d[key] = value
# check if any keys in d are already in the request query parameters
# if so, delete from d. If I misunderstood your question, then refactor
# to delete from request.GET instead:
for key in d.keys():
if key in request.GET.keys():
del d[key]
# add the keys in request.GET into d:
d.extend(request.GET.items())
# convert dictionary of key value pairs into a query string for urls,
# prepend a ?
queryString = "?%s" % urlencode(d)
return "%s%s" % (request.path, queryString if queryString else "")
I should point out that if the request is for the page http://example.com/login/?prev=news and your template looks like
My Link
Then the output would be (hopefully, if it works):
<a href="/login/?prev=news&goto=dashboard&feature=products">
My Link</a>
<!-- the order of the above parameters may differ -->
Note that there is no domain (i.e. the http://example.com/ part) in this link. That is because the filter doesn't add it. But you can modify it to do so.
I'll leave it to you to register this template filter. More here. Hope this helps.
A JavaScript solution is much suitable in this case.
Example :
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a page_number="1">First Page</a>
<a page_number="{{ page_obj.previous_page_number }}">Last Page</a>
{% endif %}
{% if page_obj.has_previous or page_obj.has_next %}
<span class="current">{{ page_obj.number }}/{{ page_obj.paginator.num_pages }}</span>
{% endif %}
{% if page_obj.has_next %}
<a page_number="{{ page_obj.next_page_number }}">Next Page</a>
<a page_number="{{ page_obj.paginator.num_pages }}">End Page</a>
{% endif %}
</span>
<script type="text/javascript">
document.querySelectorAll('.pagination a').forEach(link => {
let page_number = link.getAttribute('page_number');
const gotopageUrl = new URL(location.href);
gotopageUrl.searchParams.set("page", page_number);
link.href = gotopageUrl.href;
});
</script>
</div>

Using date in the Django url templatetag

I am trying to build a date-based URL with Django's url template tag. I have a datetime object that I can display like so:
{{block|date:"F j Y"}}
However, when I use nearly the same syntax with the url templatetag, like so:
{% url meeting block|date:"Y" %}
I get an error -- it appears that the only thing passed to url is an empty string:
... Reverse for 'meeting' with arguments '(u'',)' and arguments ...
What might I be doing wrong?
The url tag is a bit strange, and is very picky about its arguments. In particular, I don't think it evaluates any filters in its arguments.
You could try this:
{% with block|date:"Y" as blockyear %}{% url meeting blockyear %}{% endwith %}