Django : model instance url is appending to the current url - django

i have some link in a template to which i want to point to particular url.
template is accessed at url : loacalhost:8000/account/profile
{% for poll in voted_poll_list %}
<h4>{{ poll.title }} </h4>
{% endfor %}
in models.py i have created the url for the poll objects to be used in a template.
def link_url(self):
return "polls/"+ "allcat/" +str(self.id)
the problem is when a link in template is clicked it is point to the loacalhost:8000/account/profile/polls/allcat/1 instead of loacalhost:8000/polls/allcat/1 which matches to url pattern
url(r'^polls/(\w+)/(?P<pid>[-\d]+)/', 'pollsite.views.poll_detail', name='detail_poll'),
the problem is link url of object is appended to current url. how can i avoid this ?

#garnertb 's solution works, but you should probably look into using the reverse function rather than hardcoding your url.
Something like:
return reverse('detail_poll', self.id)
This will not only take care of the leading slash, but also avoid trouble if you ever start changing your url configuration.

Try leading the url with a forward slash:
def link_url(self):
return "/polls/allcat/" +str(self.id)

Related

How to replace re "." in Django template url tag

I have Django url routes that looks like this:
app_name = 'courses'
urlpatterns = [
url(r'./(?P<courseid>[0-9]+)/$', views.viewcourse, name='view_course'),
url(r'./(?P<courseid>[0-9]+)/review/$', views.reviewcourse, name='review_course')
]
The "." in the regular expression will usually be replaced by a slug, e.g.:
courses/computer-science/3/
Now I need a link in my 'view course' template to take the user to the review page, i.e:
courses/computer-science/3/review/
I could do this by simply appending review to the current url:
{{ request.path }}review
However, I would rather do it the 'correct' way using a template url tag
{% url 'courses:review_course' courseid=course.pk %}
However, this produces:
courses/30/review, which of course fails.
Is it possible to generate the correct link using the {% url %} tag without having to change the "." into a name capture parameter?
Change your urls.py :
url(r'(?P<course_type>[a-z_-]+)/(?P<courseid>[0-9]+)/$', views.viewcourse, name='view_course')
Now in your view you have access to course_type in your kwargs and you can then load whatever you want (as I have no code, i'm guessing what you're doing :)).
And in your template:
{% url 'courses:review_course' courseid=course.pk course_type=something%}

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.

New url syntax from django 1.3/dev onwards

Why did the django core developers allow the url templatetag to point directly to a django view function? (reference - https://docs.djangoproject.com/en/dev/ref/templates/builtins/#url)
{% load url from future %}
{# 1st method: pointing to a view function #}
{% url 'app_views.client' %}
{# 2nd method: pointing to a named url #}
{% url 'myapp:view-name' %}
One can already name the url in urls.py and hence use the 2nd method to point to a specific url. It doesn't feel right to allow developers to actually reference a view function directly from the template.
Does anyone know why this decision was made?
Passing a dotted view function name to the {% url %} template tag is simply the form the template tag took in the earlier days of Django, before you could name URLs. It's still supported, though as you point out, you probably wouldn't use it in a modern application.
URLs in Django are just mappings to views. Therefore, in the template, using a named URL is just indirectly referencing the view anyway.
The exception is where a single view is mapped to by multiple URLs.
Also note that they are planning to change the syntax of the url tag in 1.5. It will take a context variable as the parameter, rather than a string. It will still take views or named URLs though.

Django path to current page

I have a django template page, and want a link from this page, containing current URL, for example, I am on /article/11 and want link to /article/11/remove
I tried the following construction:
Remove article
But I get link to /article/remove instead of /article/11/remove
However when I change it to
<a href="{{ request.path }}">
I get link to /article/11
How can I get URL not trimmed?
I don't see why it doesn't point you to /article/11remove, which is what it sounds like it should do, but either way, you're missing a slash. Try <a href="{{ request.path }}/remove"> instead.
However, that's really not the right way to do it. You shouldbe using {% url 'name_of_remove_view' %} to get the url, not assuming it's going to be wherever you are plus /remove.
Edit: In that case, your problem is probably that {{ request.path }} is not outputting anything at all. That would explain why just having "remove" would take you to /article/remove, and having "" would take you to where you currently are, due to the way that relative URLs work. You might want to make sure that you have a request object at all in your template environment.

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>