Referring to databrowse urls in templates - django

I'd like to integrate django Databrowse into my application.
It comes down to pointing to databrowse urls from within template or view for enhanced drill down functionality of the databrowse.
Is there an easy way to extract the url from a databrowse object?

Well, one easy way would be to just construct the url you want, and pass it into the template:
databrowse_url = '/'.join((obj._meta.app_label, obj._meta.module_name, 'objects', str(obj.id)))
And then in the template (assuming you have databrowse situated at /databrowse:
<a href="/databrowse/{{ databrowse_url }}">
Which would give you a url like: /databrowse/app_name/model_name/objects/1.

You could recreate the databrowse urls in the format thats show in the databrowse urls.py
You might be able to get the url tag to work in your template by passing the view name + arguments.
However, if you browse the source, it looks like databrowse will add a 'url' attribute to the objects it works with.
EDIT:
Given an EasyModel instance, you can do the following:
my_easy_model_instance.url()
Most of the 'Easy' classes have a url() or urls() method on them.

Ended up writing mixin class that fetches relevant EasyInstance and reuses the url() of it:
from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse import site
class DatabrowseMixin:
def url(pyClass):
if not site.root_url:
#hack, but root_url is not set until the first databrowse view
#and resolving urlconf does not work either
site.root_url = '/databrowse/'
easy_model = EasyModel(site, pyClass.__class__)
obj = easy_model.object_by_pk(pyClass.pk)
return obj.url()
class MyModel(models.Model, DatabrowseMixin):
...
Now in my templates I can reuse my_model_instance.url tag pointing to databrowse object url.

Related

Is it possible to give external URLs names in django

I have just started naming my URL patterns in Django, so that if I want to change the URL pattern, I just have to change it in one place. e.g:
url(r'^$', HomeListView.as_view(), name='home'),
And referencing it in my templates like this:
{% url home %}
Is this possible with external links in case they change or I change the a Facebook page link for example. How would this look?
Thanks
One way to do this could be to write an external_url template tag, and have an ExternalURL model that stores them.
This would give you the advantage of being able to have the urls editable without redeploying changed code.
The disadvantage is that there will be database lookups to see those urls. Also, you would need to {% load external_urls %} in templates you want to use it in.
# models.py pseudo-code
class ExternalURL(models.Model):
name = models.CharField(unique=True)
url = models.URLField()
Your template tag might look something like:
# app/templatetags/external_url.py
#library.register()
def external_url(name):
try:
return ExternalURL.objects.get(name=name).url
except ExternalURL.DoesNotExist:
return ''
Another alternative could be to have a Context Processor that stores them all in the context, allowing you to not have to pass them explicitly into views: would be useful if you had several external urls that were used in many places within your system.
def external_urls(request):
return {
'google': 'http://www.google.com/',
# more here
}
The advantages of this is no database lookup, no requirement to load the template tag, but you will need to add it to your settings.CONTEXT_PROCESSORS. Also, you could inspect request to see if the current user may see all the urls.
If its external links, and possibility of the change then you should define it in settings or separate static url file and pass those variable with request context.
urls.py should be recommended to use only for your app specific urls.
In this SO thread you can see how to approach defining constant

Django pagination - nice urls

I did pagination in my django projet. Everything works just perfect, but my urls looks terrible, like
host:8000/?page=1
How to create nice urls like
host:8000/page/2/ or host:8000/2/
I use standard Paginator class via ListView
How to do this w/o third party code ?
If you define url pattern like this:
url(r'^/page/(?P<page>\d+)/$', 'myapp.views.list_view'),
then ListView will pass page url keyword into paginator.
Notice:
Each path segment is supposed to be a valid resource, so it's not clear what you will display on /path/ URL.
Django pagination system assumes that webpages will default to using the URL query, so it's recommended to keep it as a URL query and it's more revealing.

How to avoid hardcode URL in Django view

I am working on a simple project on Django. Currently the views that I am implementing always return a hardcode path:
def temp_view(request):
...
return render("app/detail.html")
or
def temp_view_2(request):
...
return redirect("/app/view2")
What I want to do is to get rid of the hardcode URL (for view URL and the template URL). Is there a proper way to do that?
Thanks.
Django provides a few different methods. In a view, the django.core.urlresolvers.reverse() function is most often used. A full discussion of this problem and the options provided by Django is here:
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-resolution-of-urls

What is reverse()?

When I read Django code sometimes, I see in some templates reverse(). I am not quite sure what this is but it is used together with HttpResponseRedirect. How and when is this reverse() supposed to be used?
reverse() | Django documentation
Let's suppose that in your urls.py you have defined this:
url(r'^foo$', some_view, name='url_name'),
In a template you can then refer to this url as:
<!-- django <= 1.4 -->
link which calls some_view
<!-- django >= 1.5 or with {% load url from future %} in your template -->
link which calls some_view
This will be rendered as:
link which calls some_view
Now say you want to do something similar in your views.py - e.g. you are handling some other URL (not /foo/) in some other view (not some_view) and you want to redirect the user to /foo/ (often the case on successful form submission).
You could just do:
return HttpResponseRedirect('/foo/')
But what if you want to change the URL in the future? You'd have to update your urls.py and all references to it in your code. This violates the DRY (Don't Repeat Yourself) principle and the whole idea of editing in one place only - which is something to strive for.
Instead, you can say:
from django.http import HttpResponseRedirect
from django.urls import reverse
return HttpResponseRedirect(reverse('url_name'))
This looks through all URLs defined in your project for the URL defined with the name url_name and returns the actual URL /foo/.
This means that you refer to the URL only by its name attribute - if you want to change the URL itself or the view it refers to you can do this by editing one place only - urls.py.
The existing answers are quite clear. Just in case you do not know why it is called reverse: It takes an input of a url name and gives the actual url, which is reverse to having a url first and then give it a name.
Existing answers did a great job at explaining the what of this reverse() function in Django.
However, I'd hoped that my answer shed a different light at the why: why use reverse() in place of other more straightforward, arguably more pythonic approaches in template-view binding, and what are some legitimate reasons for the popularity of this "redirect via reverse() pattern" in Django routing logic.
One key benefit is the reverse construction of a url, as others have mentioned. Just like how you would use {% url "profile" profile.id %} to generate the url from your app's url configuration file: e.g. path('<int:profile.id>/profile', views.profile, name="profile").
But as the OP have noted, the use of reverse() is also commonly combined with the use of HttpResponseRedirect. But why?
I am not quite sure what this is but it is used together with HttpResponseRedirect. How and when is this reverse() supposed to be used?
Consider the following views.py:
from django.http import HttpResponseRedirect
from django.urls import reverse
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected = question.choice_set.get(pk=request.POST['choice'])
except KeyError:
# handle exception
pass
else:
selected.votes += 1
selected.save()
return HttpResponseRedirect(reverse('polls:polls-results',
args=(question.id)
))
And our minimal urls.py:
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('<int:question_id>/results/', views.results, name='polls-results'),
path('<int:question_id>/vote/', views.vote, name='polls-vote')
]
In the vote() function, the code in our else block uses reverse along with HttpResponseRedirect in the following pattern:
HttpResponseRedirect(reverse('polls:polls-results',
args=(question.id)
This first and foremost, means we don't have to hardcode the URL (consistent with the DRY principle) but more crucially, reverse() provides an elegant way to construct URL strings by handling values unpacked from the arguments (args=(question.id) is handled by URLConfig). Supposed question has an attribute id which contains the value 5, the URL constructed from the reverse() would then be:
'/polls/5/results/'
In normal template-view binding code, we use HttpResponse() or render() as they typically involve less abstraction: one view function returning one template:
def index(request):
return render(request, 'polls/index.html')
But in many legitimate cases of redirection, we typically care about constructing the URL from a list of parameters. These include cases such as:
HTML form submission through POST request
User login post-validation
Reset password through JSON web tokens
Most of these involve some form of redirection, and a URL constructed through a set of parameters. Hope this adds to the already helpful thread of answers!
This is an old question, but here is something that might help someone.
From the official docs:
Django provides tools for performing URL reversing that match the
different layers where URLs are needed: In templates: Using the url
template tag. In Python code: Using the reverse() function. In higher
level code related to handling of URLs of Django model instances: The
get_absolute_url() method.
Eg. in templates (url tag)
2012 Archive
Eg. in python code (using the reverse function)
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
The function supports the dry principle - ensuring that you don't hard code urls throughout your app. A url should be defined in one place, and only one place - your url conf. After that you're really just referencing that info.
Use reverse() to give you the url of a page, given either the path to the view, or the page_name parameter from your url conf. You would use it in cases where it doesn't make sense to do it in the template with {% url 'my-page' %}.
There are lots of possible places you might use this functionality. One place I've found I use it is when redirecting users in a view (often after the successful processing of a form)-
return HttpResponseRedirect(reverse('thanks-we-got-your-form-page'))
You might also use it when writing template tags.
Another time I used reverse() was with model inheritance. I had a ListView on a parent model, but wanted to get from any one of those parent objects to the DetailView of it's associated child object. I attached a get__child_url() function to the parent which identified the existence of a child and returned the url of it's DetailView using reverse().
There is a doc for that
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-resolution-of-urls
it can be used to generate an URL for a given view
main advantage is that you do not hard code routes in your code.
The reverse() is used to adhere the django DRY principle i.e if you change the url in future then you can reference that url using reverse(urlname).

View referenced by two urls and url tag

I am using url tag in my template for a view, that is used by two different urls. I am getting the wrong url in one place. Is there any way to force django to retrieve different url? Why it doesn't notify my, that such conflict occured and it doesn't know what to do (since python zen says, that is should refuse temptation to guess).
Code in template:
{% url djangoldap.views.FilterEntriesResponse Entry=entry.path as filter_url %}
Code in urls:
(r'^filter_entries/(?P<Entry>.*)/$',
'djangoldap.views.FilterEntriesResponse',
{'filter_template': 'filter_entries.html',
'results_template': 'filter_results.html'}),
(r'^choose_entries/(?P<Entry>.*)/$',
'djangoldap.views.FilterEntriesResponse',
{'filter_template': 'search_entries.html',
'results_template': 'search_results.html'}),
As you can see, those two urls use the same view, but with different templates. How I can force django to retrieve former url, rather than latter?
Name your URLs by adding another item to the tuple:
(r'^choose_entries/(?P<Entry>.*)/$',
'djangoldap.views.FilterEntriesResponse',
{'filter_template': 'search_entries.html',
'results_template': 'search_results.html'},
'sensibleprefix-choose_entries') # <-- this is the name
Then you can use the name in the URL tag.