Altering one query parameter in a url (Django) - django

I have a search page that takes a variety of parameters. I want to create a new URL by just altering one parameter in the query. Is there an easy way to do this - something like:
# example request url
http://example.com/search?q=foo&option=bar&option2=baz&change=before
# ideal template code
{% url_with change 'after' %}
# resulting url
http://example.com/search?q=foo&option=bar&option2=baz&change=after
So this would take the request url, alter one query parameter and then return the new url. Similar to what can be achieved in Perl's Catalyst using $c->uri_with({change => 'after'}).
Or is there a better way?
[UPDATED: removed references to pagination]

I did this simple tag which doesn't require any extra libraries:
#register.simple_tag
def url_replace(request, field, value):
dict_ = request.GET.copy()
dict_[field] = value
return dict_.urlencode()
Use as:
<a href="?{% url_replace request 'param' value %}">
It wil add 'param' to your url GET string if it's not there, or replace it with the new value if it's already there.
You also need the RequestContext request instance to be provided to your template from your view. More info here:
http://lincolnloop.com/blog/2008/may/10/getting-requestcontext-your-templates/

So, write a template tag around this:
from urlparse import urlparse, urlunparse
from django.http import QueryDict
def replace_query_param(url, attr, val):
(scheme, netloc, path, params, query, fragment) = urlparse(url)
query_dict = QueryDict(query).copy()
query_dict[attr] = val
query = query_dict.urlencode()
return urlunparse((scheme, netloc, path, params, query, fragment))
For a more comprehensive solution, use Zachary Voase's URLObject 2, which is very nicely done.
Note:
The urlparse module is renamed to urllib.parse in Python 3.

I improved mpaf's solution, to get request directly from tag.
#register.simple_tag(takes_context = True)
def url_replace(context, field, value):
dict_ = context['request'].GET.copy()
dict_[field] = value
return dict_.urlencode()

This worked pretty well for me. Allows you to set any number of parameters in the URL. Works nice for a pager, while keeping the rest of the query string.
from django import template
from urlobject import URLObject
register = template.Library()
#register.simple_tag(takes_context=True)
def url_set_param(context, **kwargs):
url = URLObject(context.request.get_full_path())
path = url.path
query = url.query
for k, v in kwargs.items():
query = query.set_param(k, v)
return '{}?{}'.format(path, query)
Then in the template:
<a href="{% url_set_param page=last %}">

There are a number of template tags for modifying the query string djangosnippets.org:
http://djangosnippets.org/snippets/553/
http://djangosnippets.org/snippets/826/
http://djangosnippets.org/snippets/1243/
I would say those are the most promising looking. One point in all of them is that you must be using django.core.context_processors.request in your TEMPLATE_CONTEXT_PROCESSORS.

You can try https://github.com/dcramer/django-paging

In addition to the snippets mentioned by Mark Lavin, Here's a list of other implementations I could find for a Django template tag which modifies the current HTTP GET query string.
On djangosnippets.org:
#2237 Manipulate URL query strings using context variables using a template tag by JHsaunders
#2332 Querystring Builder - create urls with GET params by jibberia
my favorite: #2413 Yet another query string template tag by atms
#2428 Add GET parameters from current request by naktinis
On PyPI:
django-spurl by Jamie Matthews
django-urltags by Calloway Project/Corey Oordt
the add_query_param filter in django-rest-framework by Tom Christie
On GitHub:
update_querystring by David Gouldin

Related

using two templates from one view

I am trying to present content from a view in two ways: html and csv download. The only way I was able to do it was to use 2 different views, one for html presentation and one for csv. This duplicates my code and I am looking for a more elegant solution.
Any suggestions?
Here is the sample code:
# views.py
[...]
def member_list(request):
member_list = Member.objects.all()
return render_to_response("member_list.html",
{'member_list':member_list)
def member_csv_list(request):
member_list = Member.objects.all()
csv_list = HttpResponse(content_type='text/csv')
csv_list['Content-Disposition'] = 'attachment; filename="member_list.csv"'
writer = csv.writer(csv_list)
writer.writerow(['Name', 'Member Type', 'Rooms'])
for member in member_list:
fields = [member.name, member.member_type, member.room]
writer.writerow(fields)
return member_list
You can use a parameter in your url and implement a view like
def myview(request) :
type = request.GET.get('type', 'html')
# do processing
if type == 'html':
# return html
else if type == 'csv':
# return csv
If you access a url like http://yourserver/myview?type=csv it will render the csv part of the view. When the url http://yourserver/myview is accessed it will return the html part of the view.
Rohan's answer is absolutely the right paradigm. For an excellent tutorial-style introduction to this topic, cf. Multiple Templates in Django.
Here are a few quotes (all credit goes to Scott Newman).
To serve a printable version of an article, for example, we can add ?printable to the end of the URL.
To make it work, we'll add an extra step in our view to check the URL for this variable. If it exists, we'll load up a printer-friendly template file. If it doesn't exist, we'll load the normal template file.
def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=pid)
if request.GET.has_key('printable'):
template_file = 'press/detail_printable.html'
else:
template_file = 'press/detail.html'
t = loader.get_template(template_file)
c = Context({'press': p})
return HttpResponse(t.render(c))
He continues with template overrides and different templates by domain names. All this is excellent.

Django Python Appengine

I came across this tutorial:
http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/
Fantastic!
Everything worked.
I just did not fully understand the code below because in comparison to Django it seems different:
views.py:
def main(request):
visitor = Visitor()
visitor.ip = request.META["REMOTE_ADDR"]
visitor.put()
result = ""
visitors = Visitor.all()
visitors.order("-added_on")
for visitor in visitors.fetch(limit=40):
result += visitor.ip + u" visited on " + unicode(visitor.added_on) + u""
return HttpResponse(result)
#model.py:
from google.appengine.ext import db
class Visitor(db.Model):
ip = db.StringProperty()
added_on = db.DateTimeProperty(auto_now_add=True)
What exactly is Visitor() ? A tuple a list?
And what does visitor.ip , visitor.put(), visitors.fetch() do exactly?
I believe:
visitor.ip saves the request.META["REMOTE_ADDR"] in the db field.
visitor.put() saves it.
visitors.fetch(limit = 40) extracts it from the db.
What I was trying to do is a tenplate that lists every IP below the next one.
I believed:
<p><ol><Li> {{ result }} </li></ol></p>
Would do the trick.
But it didn't.
Thanks !
Visitor is a class, and each field in it represents a column in your database.
When you do visitor = Visitor() you're essentially creating a new row in your database. Calling visitor.put() is what actually commits it into the database. Visitors.all() returns a all the rows in the db (it's either a list, tuple, or dictionary), so then visitors.fetch() is just an operation on that.
The reason your template isn't working is because your function in views.py isn't specifying any template. This is taken from the Django tutorial: http://docs.djangoproject.com/en/1.0/intro/tutorial03/
from django.template import Context, loader
from mysite.polls.models import Poll
from django.http import HttpResponse
def index(request):
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
t = loader.get_template('polls/index.html')
c = Context({
'latest_poll_list': latest_poll_list,
})
return HttpResponse(t.render(c))
The parameter for Context() is a dictionary. The string on the left is what the name of the variable will be within the template, and the right side is what actual variable that it corresponds to. In your example you can use {'mylist': result}, and in your template you could use {{ mylist }} instead of {{ result }}
You also need to make sure to set a template directory in settings.py which the template (in the above example) is polls/index.html within that template dir.
Without knowing anything about the app engine, I'd say this: Visitor() returns an instance of the Visitor class. The step that follows (visitor.ip = request.META["REMOTE_ADDR"]) sets an attribute of the instance created in the first line.

Django templatetag "order of processing"

I am trying to write a set of template tags that allow you to easily specify js and css files from within the template files themselves. Something along the lines of {% requires global.css %}, and later in the request, {% get_required_css %}.
I have this mostly working, but there are a couple of issues. We'll start with the 'timing' issues.
Each template tag is made up of two steps, call/init and render. Every call/init happens before any render procedure is called. In order to guarantee that all of the files are queued before the {% get_required_css %} is rendered, I need to build my list of required files in the call/init procedures themselves.
So, I need to collect all of the files into one bundle per request. The context dict is obviously the place for this, but unfortunately, the call/init doesn't have access to the context variable.
Is this making sense? Anyone see a way around this (without resorting to a hack-y global request object)?
Another possibility to store these in a local dict but they would still need to be tied to the request somehow... possibly some sort of {% start_requires %} tag? But I have no clue how to make that work either.
I've come up with a way to do this which more suits your needs. It will have a bit more load on the server, but proper caching can help to alleviate most of that. Below I've outlined a way that should work if the CSS includes are the same for each path. You'll need to create a single view to include all of these files, but you can actually optimize your CSS using this method, making only a single CSS call for each page.
import md5
class LoadCss(template.Node):
def __init__(self, tag_name, css):
self.css = css
self.tag_name = tag_name
def render(self, context):
request = context['request']
md5key = md5.new(request.path).hexdigest()
if md5key not in request.session:
request.session[md5key] = list()
## This assumes that this method is being called in the correct output order.
request.session[md5key].append(self.css)
return '<!-- Require %s -->' % self.css
def do_load_css(parser, token):
tag_name, css = token.split_contents()
return LoadCss(tag_name, key)
register.tag('requires', do_load_css)
class IncludeCss(template.Node):
def __init__(self, tag_name):
self.tag_name = tag_name
def render(self, context):
request = context['request']
md5key = md5.new(request.path).hexdigest()
return '<link rel="stylesheet" href="/path/to/css/view/%s">' % md5key
def do_include_css(parser, token):
return IncludeCss(token)
register.tag('get_required_css', do_include_css)
views.py:
from django.conf import settings
from django.views.decorators.cache import cache_page
import os
#cache_page(60 * 15) ## 15 Minute cache.
def css_view(request, md5key):
css_requires = request.session.get(md5key, list())
output = list()
for css in css_requires:
fname = os.path.join(settings.MEDIA_ROOT, 'css', css) ## Assumes MEDIA_ROOT/css/ is where the CSS files are.
f = open(fname, 'r')
output.append(f.read())
HttpResponse(''.join(output), mimetype="text/css")
This allows you to store the CSS information in the context, then in the session, and serve the output from a view (with caching to make it faster). This will, of course, have a bit more server overhead.
If you need to vary the CSS on more than just the path, then you can simply modify the md5 lines to suit your needs. You have access to the entire request object, and the context, so almost everything should be in there.
Beware: On second review, this may cause a race condition if the browser fetches the CSS before the session has been populated. I do not believe Django works that way, but I don't feel like looking it up right now.

How do I construct a Django reverse/url using query args?

I have URLs like http://example.com/depict?smiles=CO&width=200&height=200 (and with several other optional arguments)
My urls.py contains:
urlpatterns = patterns('',
(r'^$', 'cansmi.index'),
(r'^cansmi$', 'cansmi.cansmi'),
url(r'^depict$', cyclops.django.depict, name="cyclops-depict"),
I can go to that URL and get the 200x200 PNG that was constructed, so I know that part works.
In my template from the "cansmi.cansmi" response I want to construct a URL for the named template "cyclops-depict" given some query parameters. I thought I could do
{% url cyclops-depict smiles=input_smiles width=200 height=200 %}
where "input_smiles" is an input to the template via a form submission. In this case it's the string "CO" and I thought it would create a URL like the one at top.
This template fails with a TemplateSyntaxError:
Caught an exception while rendering: Reverse for 'cyclops-depict' with arguments '()' and keyword arguments '{'smiles': u'CO', 'height': 200, 'width': 200}' not found.
This is a rather common error message both here on StackOverflow and elsewhere. In every case I found, people were using them with parameters in the URL path regexp, which is not the case I have where the parameters go into the query.
That means I'm doing it wrong. How do I do it right? That is, I want to construct the full URL, including path and query parameters, using something in the template.
For reference,
% python manage.py shell
Python 2.6.1 (r261:67515, Feb 11 2010, 00:51:29)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.core.urlresolvers import reverse
>>> reverse("cyclops-depict", kwargs=dict())
'/depict'
>>> reverse("cyclops-depict", kwargs=dict(smiles="CO"))
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Library/Python/2.6/site-packages/django/core/urlresolvers.py", line 356, in reverse
*args, **kwargs)))
File "/Library/Python/2.6/site-packages/django/core/urlresolvers.py", line 302, in reverse
"arguments '%s' not found." % (lookup_view_s, args, kwargs))
NoReverseMatch: Reverse for 'cyclops-depict' with arguments '()' and keyword arguments '{'smiles': 'CO'}' not found.
Building an url with query string by string concatenation as suggested by some answers is as bad idea as building SQL queries by string concatenation. It is complicated, unelegant and especially dangerous with a user provided (untrusted) input. Unfortunately Django does not offer an easy possibility to pass query parameters to the reverse function.
Python standard urllib however provides the desired query string encoding functionality.
In my application I've created a helper function:
def url_with_querystring(path, **kwargs):
return path + '?' + urllib.urlencode(kwargs) # for Python 3, use urllib.parse.urlencode instead
Then I call it in the view as follows:
quick_add_order_url = url_with_querystring(reverse(order_add),
responsible=employee.id, scheduled_for=datetime.date.today(),
subject='hello world!')
# http://localhost/myapp/order/add/?responsible=5&
# scheduled_for=2011-03-17&subject=hello+world%21
Please note the proper encoding of special characters like space and exclamation mark!
Your regular expresion has no place holders (that's why you are getting NoReverseMatch):
url(r'^depict$', cyclops.django.depict, name="cyclops-depict"),
You could do it like this:
{% url cyclops-depict %}?smiles=CO&width=200&height=200
URLconf search does not include GET or POST parameters
Or if you wish to use {% url %} tag you should restructure your url pattern to something like
r'^depict/(?P<width>\d+)/(?P<height>\d+)/(?P<smiles>\w+)$'
then you could do something like
{% url cyclops-depict 200 200 "CO" %}
Follow-up:
Simple example for custom tag:
from django.core.urlresolvers import reverse
from django import template
register = template.Library()
#register.tag(name="myurl")
def myurl(parser, token):
tokens = token.split_contents()
return MyUrlNode(tokens[1:])
class MyUrlNode(template.Node):
def __init__(self, tokens):
self.tokens = tokens
def render(self, context):
url = reverse('cyclops-depict')
qs = '&'.join([t for t in self.tokens])
return '?'.join((url,qs))
You could use this tag in your templates like so:
{% myurl width=200 height=200 name=SomeName %}
and hopefully it should output something like
/depict?width=200&height=200&name=SomeName
I recommend to use builtin django's QueryDict. It also handles lists properly. End automatically escapes some special characters (like =, ?, /, '#'):
from django.http import QueryDict
from django.core.urlresolvers import reverse
q = QueryDict('', mutable=True)
q['some_key'] = 'some_value'
q.setlist('some_list', [1,2,3])
'%s?%s' % (reverse('some_view_name'), q.urlencode())
# '/some_url/?some_list=1&some_list=2&some_list=3&some_key=some_value'
q.appendlist('some_list', 4)
q['value_with_special_chars'] = 'hello=w#rld?'
'%s?%s' % (reverse('some_view_name'), q.urlencode())
# '/some_url/?value_with_special_chars=hello%3Dw%23rld%3F&some_list=1&some_list=2&some_list=3&some_list=4&some_key=some_value'
To use this in templates you will need to create custom template tag
Working variation of previous answers and my experience with dealing this stuff.
from django.urls import reverse
from django.utils.http import urlencode
def build_url(*args, **kwargs):
params = kwargs.pop('params', {})
url = reverse(*args, **kwargs)
if params:
url += '?' + urlencode(params)
return url
How to use:
>>> build_url('products-detail', kwargs={'pk': 1}, params={'category_id': 2})
'/api/v1/shop/products/1/?category_id=2'
The answer that used urllib is indeed good, however while it was trying to avoid strings concatenation, it used it in path + '?' + urllib.urlencode(kwargs). I believe this may create issues when the path has already some query parmas.
A modified function would look like:
def url_with_querystring(url, **kwargs):
url_parts = list(urlparse.urlparse(url))
query = dict(urlparse.parse_qsl(url_parts[4]))
query.update(kwargs)
url_parts[4] = urllib.urlencode(query)
return urlparse.urlunparse(url_parts)
Neither of the original answers addresses the related issue resolving URLs in view code. For future searchers, if you are trying to do this, use kwargs, something like:
reverse('myviewname', kwargs={'pk': value})

passing object data through URL

I know that I can pass object values through a URL pattern and use them in view functions. For instance:
(r'^edit/(?P<id>\w+)/', edit_entry),
can be utilized like:
def edit_entry(request, id):
if request.method == 'POST':
a=Entry.objects.get(pk=id)
form = EntryForm(request.POST, instance=a)
if form.is_valid():
form.save()
return HttpResponseRedirect('/contact/display/%s/' % id)
else:
a=Entry.objects.get(pk=id)
form = EntryForm(instance=a)
return render_to_response('edit_contact.html', {'form': form})
But how do I pass a value from a model field (other than "id") in the url? For instance, I have an abstract base model with a field "job_number" that is shared by child models "OrderForm" and "SpecReport". I want to click on the "job_number" on the order form and call the Spec Report for that same job number. I can create an
href="/../specifications/{{ record.job_number }}
to pass the info to the url, but I already know that this regex syntax is incorrect:
(r'^specifications/(?P<**job_number**>\w+)/', display_specs),
nor can I capture the job_number in the view the same way I could an id:
def display_specs(request, job_number):
records = SpecReport.objects.filter(pk=job_number)
tpl = 'display.html'
return render_to_response(tpl, {'records': records })
Is there an easy approach to this or is it more complicated than I think it is?
the amended code is as follows:
(r'^specdisplay/?agencyID=12/', display_specs),
and:
def display_specs(request, agencyID):
agencyID= request.GET.get('agencyID')
records = ProductionSpecs.objects.filter(pk=id)
tpl = 'display_specs.html'
return render_to_response(tpl, {'records': records })
not sure how to filter. pk is no longer applicable.
Yes, you are making this a little more complicated that it is.
In your urls.py you have:
(r'^edit/(?P<id>\w+)/', edit_entry),
Now you just need to add the almost identical expression for display_specs:
(r'^specifications/(?P<job_number>\w+)/', display_specs),
Parenthesis in the regex identifies a group and the (?P<name>...) defines a named group which will be named name. This name is the parameter to your view.
Thus, your view will now look like:
def display_specs(request, job_number):
...
Finally, even though this will work, when you redirect to the view, instead of using:
HttpResponseRedirect('/path/to/view/%s/' % job_number)
Use the more DRY:
HttpResponseRedirect(
reverse('display_specs', kwargs={'job_number': a.job_number}))
Now if you decide to change your resource paths your redirect won't break.
For this to work you need to start using named urls in your urlconf like this:
url(r'^specifications/(?P<job_number>\w+)/', display_specs, name='display_specs'),
Not knowing what your model structure is like ... why couldn't you just pass the particular job's id and then pick it up with a query?
Afaik every model automatically has an id field that autoincrements and is a unique identifier of a row (an index if you will), so just change the href creation to {{record.id}} and go from there.
Try passing the job_number through the url then, especially if you don't care about pretty url's too much just do this:
url: /foo/bar/?job_number=12
no special markup to catch this btw, the regex is r'^foo/bar/'
And then read it in the view like this:
job_number= request.GET.get('job_number')
I really don't understand your question. What's the difference between passing id and passing job_number in a URL? If you can do one, why can't you do the other? And once the job_number is in the view, why can't you do a normal filter:
records = SpecReport.objects.filter(job_number=job_number)