I have a database with blog style documents, i.e., author's name, publication date, body text, etc.
I have built a django framework to output entries from the database as a result of a search term. That part is ok. The problem is that I want to show sections of the body text with the matched search terms highlighted (equivalent to a google search result). This means that I can't create a template tag with the body_text attribute only, because that text is not highlighted. I already did a function that receives as input the query and the body text and outputs the same text with the found search terms in bold.
My problem now is how do I pass this result to the html template?
Using the tutorial from Django documentation suppose you have the following views.py:
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
and the correspondent template:
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li>{{ question.question_text }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
Now suppose you have the function in views.py:
def signal_tokens(text,query_q):
...
return new_text
What should be the best way to replace the {{ question.question_text } with the output from signal_tokens? My solution was to replicate the context variable with a list of dictionaries, where each dictionary is a copy of each entry, except for the 'question_text' key, where I used signal_tokens result:
def index(request):
query_q = 'test'
latest_question_list = Question.objects.order_by('-pub_date')[:5]
new_context = []
for entry in latest_question_list:
temp_d = {}
temp_d['id'] = entry.id
temp_d['question_text'] = signal_tokens(entry.question_text,query_q)
new_context.append(temp_d)
context = {'latest_question_list': new_context}
return render(request, 'polls/index.html', context)
But the problem is that I need to copy all entries. Is there a more elegant way to solve this problem?
This is an ideal use case for a template filter. Move your highlight code to a file in the templatetags directory, register it as a filter, then you can call it from the template:
{{ question.question_text|highlight:query_q }}
Obviously you will need to pass query_q to the template context as well.
Related
I have a problem when transfering two list to a html in django.
I hope that it will be a:1b:2Each of two data is paired.
But it does not work. There have some demo codes, showing the same error.
Absolutly,it has no error log,because the {{l2s.order}} is null,and the system does not throw error.Maybe my poor English makes it confusing,If there are any where no description clearly, please point out, I'll add the appropriate information。
Thanks for your help.
# fake view
def test(request):
l1s = ["a","b","c","d","e","f"]
l2s = ["1","2","3","4","5","6"]
return render(request,'fake.html',locals())
# fake html
{% for l1 in l1s% }
{% with order=forloop.counter0 %}
{{l1}}-{{l2s.order}}
{% endfor %}
You can use the built in zip method for that
views.py
def test(request):
l1s = ["a","b","c","d","e","f"]
l2s = ["1","2","3","4","5","6"]
context['data'] = zip(l1s, l2s)
return render(request,'fake.html',context)
fake.html
{% for item in data %}
{{item[0]}} : {{ item[1] }}
{% endfor %}
I want to access the elements of a list using Jinja.
Here in the below code both "id" and images are list.
image_name is the field that stores the image
{% for blog in id%}
<h3>{{blog.news_title}}</h3><br/>
<img src="images[loop.index0].image_name"/><br/>
<time>{{blog.news_date}}</time><br/>
click here<br/>
{% endfor%}</li>
Views.py
def BlogViews(request,blog_type):
"""
The blogs are displayed according to the latest, current-month and last-month classification
"""
blog_type=blog_type.replace('-','_')
response_blog=requests.get("API" % (settings.BASE_URL,blog_type),headers=headers,verify=False)
if(response_blog.status_code==200):
data_blog=response_blog.json()
if(data_blog['ErrorCode']==0 and data_blog['SubErrorCode']==0):
blog=BlogYearViews()
blog_images=BlogImages(request,data_blog)
return render(request,"CombinedBlog.html",{"id":data_blog['Data'],"years":blog,"images":blog_images})
else:
return render(request,"Page404.html")
def BlogImages(request,data_blog):
"""
Returns a list of all the images
"""
data_image_list=[]
for i in range(0,len(data_blog['Data'])):
images=data_blog['Data'][i]['image_id']
response_image=requests.get("API"%(settings.BASE_URL),headers=headers,verify=False)
data_image=(response_image.json())
data_image_list=data_image_list+data_image['Data']
return (data_image_list)
You need to zip the two lists together in your view and iterate through them in the template.
blog_images = BlogImages(request, data_blog)
blogs_and_images = zip(data_blog['Data'], blog_images)
return render(request, "CombinedBlog.html", {"blogs_and_images": blogs_and_images, "years":blog})
...
{% for blog, image in blogs_and_images %}
<h3>{{ blog.news_title }}</h3><br/>
<img src="{{ image.image_name }}"/><br/>
<time>{{ blog.news_date }}</time><br/>
click here<br/>
{% endfor %}</li>
Note, you really should be using the {% url %} tag to create the hrefs rather than building it manually like that. Also note, your BlogImages function doesn't need to take the request, since it never uses it, and is anyway extremely un-Pythonic. It should look like this:
def blog_images(data_blog):
data_image_list = []
for blog in data_blog['Data']:
image_id = blog['image_id']
response_image = requests.get("API" % settings.BASE_URL, headers=headers, verify=False)
data_image = response_image.json()
data_image_list.append(data_image['Data'])
return
data_image_list
To access images list using index you can use forloop.counter.
You can use either:
{{ forloop.counter }} # index starts at 1
{{ forloop.counter0 }} # index starts at 0.
In template, you can do:
<img src="{{images[forloop.counter0].image_name.url}}"/>
Advice:
You should consider renaming blog lists as blogs or blog_list inplace of id.
My template renders the tag {{ test.date}} in the following format -
2015-12-15T23:55:33.422679
When I try to format it using django's built in template tag date, it doesn't display anything.
Variations I've tried:
{{ test.date|date:"SHORT_DATE_FORMAT" }}
{{ test.date|date:"D d M Y" }}
models.py:
class Human(models.Model):
name = models.CharField(max_length=50,default='',blank=False)
class Test(models.Model):
human = models.ForeignKey(Human)
date = models.DateTimeField(default=datetime.now)
views.py:
def list(request):
h = Human.objects.all()
s=[]
for her in h:
t = h.test_set.all()
s.extend(t)
context = RequestContext(request, {'test_list': s,})
return render_to_response('template.html', context)
I am then using it in template like this:
{% for test in test_list %}
{{test.date}}
{% endfor %}
What am I missing?
Answering an OLD post... but, the answer doesn't seem (to me) to be answering the original question - which was WHY isn't the Django inline template date formatting working...
The answer is (I believe) that the author was trying to output to his page something like:
"This is my date:{{test.date|date:"D d M Y"}}."
The problem, if this is truly what was being tried, is that the double quotes don't work in this situation. You need to do the following instead:
"This is my date:{{test.date|date:'D d M Y'}}."
Note the single quotes...
I'm not sure what you want from this logic, but I think you can use this:
def list(request):
test = Test.objects.all()
return render(request, 'template.html', {'test':test})
and in template:
{% for t in test %}
{% t.date %}
{% endfor %}
if you want display human, just add in cycle {% t.human.name %}
I am trying out to build full-text searching by using sphinx search, postgresql & django based on this tutorial: http://pkarl.com/articles/guide-django-full-text-search-sphinx-and-django-sp/.
All setup done for sphinx & postgresql and it works but I got trouble when reach on Sample Django code part.
In django views & urlconf, I only changed the function of *search_results* into search and Story model with my own model. For URLConf, I only changed *search_results* into search just same like on views and nothing changed made on search template.
So when I try to search from my form in Django, I get exception:
TypeError at /search/
list() takes exactly 1 argument (0 given)
I also try to changed based on steyblind's comment by change the urlpattern & view definition like this:
(r'^search/(.*)?', search),
def search(request, query=''):
but still keep get TypeError exception.
Is there any mistake I am doing here? Thanks in advance.
Here's my snippets:
Urls.py
(r'^search/(.*)', search),
Views.py
def search(request, query):
try:
if(query == ''):
query = request.GET['query']
results = Flow.search.query(query)
context = { 'flows': list(results),'query': query, 'search_meta':results._sphinx }
except:
context = { 'flows': list() }
return render_to_response('search.html', context, context_instance=RequestContext(request))
search.html
{% extends "base.html" %}
{% block main %}
<div>
<form action="/search/" method="GET">
<input type="text" name="query"/>
<input type="submit">
</form>
{% if flows %}
<p>Your search for “<strong>{{ query }}</strong>” had <strong>{{ search_meta.total_found }}</strong> results.</p>
<p>search_meta object dump: {{ search_meta }}</p>
{% endif %}
<hr/>
{% for s in flows %}
<h3>{{ s.title }}</h3>
<p>(weight: {{ s.sphinx.weight }})</p>
<p>story.sphinx object dump: {{ s.sphinx }}</p>
{% empty %}
<p>YOU HAVEN'T SEARCHED YET.</p>
{% endfor %}
</div>
{% endblock %}
Correct me if I'm wrong, but Django-Sphinx seems to be an abandoned project. The last update to it was a year ago, with most updates being 3-5 years ago. Also, I cannot speak for Django then, but it can do now, out of the box, what you are trying to do with Sphinx.
What version of Django and Python are you using? The error you are getting is strange as list() can take no arguments. Try this in a python shell:
>> list()
[]
Regardless, I've made a few modifications to the code that could possibly help fix the issue. However, if there are no results, you are passing 'flows' as empty in this line:
context = { 'flows': list() }
If you look at the template, this really accomplishes nothing.
urls.py:
(r'^search/', search),
views.py:
def search(request):
query = request.GET.get('query')
if query:
results = Flow.search.query(query)
if results:
context = { 'flows': list(results),'query': query, 'search_meta':results._sphinx }
else:
context = { 'flows': list() }
return render_to_response('search.html', context, context_instance=RequestContext(request))
All that said, I'd highly suggest that since this project is so outdated that you use your own search. Or if you need more functionality, you could use a search app like Haystack which is updated frequently. Using the same urls.py as above, you could implement the below as an easy search that will return all results for a blank search, the actual filtered results for a query.
views.py:
def search(request):
query = request.GET.get('q')
results = Flow.objects.all()
if query:
results = results.query(query)
return render_to_response('search.html', {"flows": results,}, context_instance=RequestContext(request))
I'm trying to do pagination with the page parameter in the URL (instead of the GET parameter). I also want my pagination to be shared code across multiple different templates.
Given that, I think I need to do something like this :
urls.py:
url(r'^alias/page;(?P<page>[0-9]+)/(?P<id>.*)$', alias.get, name="alias"),
tempaltes/alias.html:
<div>...stuff...</div>
{% include "paginator.html" %}
templates/paginator.html :
{% if page_obj.has_previous or page_obj.has_next %}
{% load filters %}
<div class="pagination clear">
{% if page_obj.has_previous %}
‹‹ previous
...
What is somemagic?
Assume I want to keep my url the same except set the page page_obj.previous_page_number
Edit:
You need somemagic to be a variable with the name of the current view.
Try this:
{% with request.path_info|resolve_url_name as current_view %}
{% url current_view page_obj.previous_page_number object.id %}
{% endwith %}
You can get this working with some code from django-snippets:
Variable resolving URL template tag Makes the {% url %} tag resolve variables from context.
Resolve URLs to view name The function resolve_to_name(path) returns the view name for path. You just need to create a filter that uses this function.
This solution wont work with urls like:
'alias/param1_regexp/param2_regexp/page;(?P<page>[0-9]+)/(?P<id>.*)$'
because you've no clue about param1 and param2.
A modification can be done to the django-snippets above to make this kind of urls work:
First snippet modifications:
from django.template import defaulttags, VariableDoesNotExist, Variable
class ResolvingURLNode(defaulttags.URLNode):
def render(self, context):
original_view_name = self.view_name
try:
resolved = Variable(self.view_name).resolve(context)
if len(resolved) > 1:
self.view_name = resolved[0]
if resolved[1]:
self.args = [Variable(arg) for arg in resolved[1]]
elif len(resolved) > 0:
self.view_name = resolved[0]
else:
self.view_name = resolved
except VariableDoesNotExist:
pass
ret = super(defaulttags.URLNode, self).render(context)
# restore view_name in case this node is reused (e.g in a loop) in
# which case the variable might resolve to something else in the next iteration)
self.view_name = original_view_name
return ret
defaulttags.URLNode = ResolvingURLNode
Second snippet modifications
from django.core.urlresolvers import RegexURLResolver, RegexURLPattern, Resolver404, get_resolver
__all__ = ('resolve_to_name',)
def _pattern_resolve_to_name(self, path):
match = self.regex.search(path)
if match:
name = ""
if self.name:
name = self.name
elif hasattr(self, '_callback_str'):
name = self._callback_str
else:
name = "%s.%s" % (self.callback.__module__, self.callback.func_name)
if len(match.groups()) > 0:
groups = match.groups()
else:
groups = None
return name, groups
def _resolver_resolve_to_name(self, path):
tried = []
match = self.regex.search(path)
if match:
new_path = path[match.end():]
for pattern in self.url_patterns:
try:
resolved = pattern.resolve_to_name(new_path)
if resolved:
name, groups = resolved
else:
name = None
except Resolver404, e:
tried.extend([(pattern.regex.pattern + ' ' + t) for t in e.args[0 ['tried']])
else:
if name:
return name, groups
tried.append(pattern.regex.pattern)
raise Resolver404, {'tried': tried, 'path': new_path}
# here goes monkeypatching
RegexURLPattern.resolve_to_name = _pattern_resolve_to_name
RegexURLResolver.resolve_to_name = _resolver_resolve_to_name
def resolve_to_name(path, urlconf=None):
return get_resolver(urlconf).resolve_to_name(path)
Basically, resolve_to_name returns the name of the view and it's parameters as a tuple, and the new {% url myvar %} takes this tuple and uses it to reverse the path with the view name and it's parameters.
If you don't like the filter approach it can also be done with a custom middleware.
Previous answer
You should check django-pagination, it's a really nice django application, easy tu use and gets the job done.
With django pagination the code to paginate an iterable would be:
{% load pagination_tags %}
{% autopaginate myiterable 10 %} <!-- 10 elements per page -->
{% for item in myiterable %}
RENDERING CONTENT
{% endfor %}
{% paginate %} <!-- this renders the links to navigate through the pages -->
myiterable can be anything that is iterable:list, tuple, queryset, etc
The project page at googlecode:
http://code.google.com/p/django-pagination/
It will be something like the following. Except I don't know what you mean by id so I just put a generic object id. The syntax for url is {% url view_name param1 param2 ... %}
{% url alias page_obj.previous_page_number object.id %}
Updated base on your need:
{% url alias page_obj.previous_page_number object.id as prev_url %}
{% include "paginator.html" %}
...
{% if page_obj.has_previous %}
‹‹ previous