I am using Django 2 and I would like to display in a single article blog page both the body of that article and, at the bottom, 3 recommended articles.
Unfortunately the display 3 articles part doesn't work. I receive no error it just doesn't display any part of the block from the loop more specifically:
my view
def detail (request, post_slug):
post = get_object_or_404 (Post, slug=post_slug)
suggested = Post.objects.all()[:3]
return render (request, 'detail.html', {'post':post}, {'suggested':suggested})
and the html to display the suggested
<section class="read-next">
{% for a in suggested.all %}
<a href="/{{a.slug}}" class="prev-post " style="background-image:url({{a.image}})" >
<div class="info">
<div class="tag">We recommend</div>
<h3 class="post-title">{{a.title}}</h3>
</div>
</a>
{% endfor %}
</section> <!-- .read-next -->
nothing gets rendered of this section or for wherever I add the loop.
Thanks in advance for your help!
A couple things are the issue here.
render is called wrong. The format of render is like so:
return render(request, template_name, context)
context is a single dictionary that can be used to place variable values on the html page.
You are sending two separate dictionaries to render. So context right now for you is only a dictionary with one key: "post". The dictionary that contains suggested is set to content_type instead of being sent to context.
So your view needs to become:
def detail (request, post_slug):
post = get_object_or_404 (Post, slug=post_slug)
suggested = Post.objects.all()[:3]
context = {
"post": post,
"suggested": suggested
}
return render (request, 'detail.html', context)
Because you sliced the queryset of Post objects (suggested = Post.objects.all()[:3]), it is now a queried list, not a usable queryset. So you treat it as if it's a list. Basically what this means, is that you don't use {% for a in suggested.all %} because suggested no longer has a method called all after slicing it.
So your template should use {% for a in suggested %} instead of {% for a in suggested.all %}. The reason it didn't work this correct way before is because suggested wasn't even in the context before because of issue #1.
When you call render you should return a single context dictionary:
return render(request, 'detail.html', {'post':post, 'suggested':suggested})
The fourth argument to the render shortcut is content_type, so your current code is equivalent to:
return render(request, 'detail.html', context={'post':post}, content_type={'suggested':suggested})
Related
So since i've been trying to learn django there is one thing that confuses me more than anything else, and that is getting something that works in the django shell into a format that works in models.py of views.py. So here is an example from the Documentation:
>>> Entry.objects.all().filter(pub_date__year=2006)
So I can work with the shell, there are loads of examples everywhere, what never seems to be covered is how you then put this in your code if you want to filter for other years for example. Can someone explain this or point me at documentation that explains this, as I've not found it in the django docs.
There are several ways to do that, but for the beginning and learning purposes your view should accept a year argument:
def my_view(request, year):
entries = Entry.objects.filter(pub_date__year=year)
context = {
"entries": entries
}
return TemplateResponse(request, 'my_template.html', context)
The line you are talking about is used to query the database and then filter things. This line is mostly used in views or serializers since you want the entries to be passed on to either template or as JSON to API response.
Using it in views:
def view(request):
entries = Entry.objects.filter(pub_date__year=year)
return render(request, 'index.html', {'entries': entries})
Here the object {'entries': entries} in above code is context object.
And then after that, you can use the same thing in Django Templates for example here in index.html
{% for entry in entries %}
<li> {{ entry.id }} </li>
{% endfor %}
I am using django-filter to filter a ListView and would like to display a "Clear all filters" link if any filters are applied.
Due to the generic nature of the filtering system I haven't yet found a straightforward way to achieve this.
The only thing I came up with so far is to return the regular queryset in the get_queryset method of the view if a "clear" flag is present in the request, however this doesn't actually clear the filters - it just returns all the data.
Does anyone have a solution/idea for this?
Update: Solution
After Jerin's comment I decided to solve this problem in 2 separate parts:
has filter:
I check if any of the fields I defined in my filter class are in the request. My solution looks a bit different as I'm using class based views so I abstracted it away in a mixin but if you're using simple views like here, you could just do:
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
has_filter = any(field in request.GET for field in
set(f.get_fields()))
return render(request, 'my_app/template.html', {
'filter': f,
'has_filter': has_filter
})
clear all filters:
A simple redirect to your list view:
{% if has_filter %}
{% trans 'Clear all filters' %}
{% endif %}
Here is the mixup version of the answer (combination of mine and Chris)
You could place a Clear all filters button and that will redirect to your default ListView (/host/end/point/).
But some non-filter parameters (such as pagination or something else) may occur in URL. So the better option is, check for any filter fields in URL and if so, display the filter clearing link
The opted solution is,
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
has_filter = any(field in request.GET for field in set(f.get_fields()))
return render(request, 'my_app/template.html', {
'filter': f,
'has_filter': has_filter
})
and in template,
{% if has_filter %}
{% trans 'Clear all filters' %}
{% endif %}
Just make a button and point to the base search field.
<a class="btn btn-warning" href="{% url 'App:FilterView' %}">Reset</a>
If your FilterSet instance is available on the template you can check for filter.is_bound like this:
{% if filter.is_bound %}
Clear filters
{% endif %}
If you are using the FilterMixin or the FilterView, your FilterSet instance will be available as filter to the template as above.
I like this simple solution, however when I attempt to use it the current filter parameters are some how getting appended to the url even though it's the base url in the anchor.
So hovering over the button my link (determined using {% url 'app:view' %} shows
localhost/app/view correctly
However when clicking the button the url in browser has the parameters appended
localhost/app/view/?filter1=val1&filter2=val2 etc.
Is django caching something? Is browser (Chrome) caching? Can I force something in the anchor to not use them?
Answered my own, but for anyone else passing by:
I had the anchor on a button within a the filter form, although it was not a submit button moving it outside the form gave the desired result.
I have about 4-5 templates for my Django project. I want to use each template across about 10 pages(with 10 different sets of content/text) each.
I've been using contexts to fill out the relevant content on each page when it is rendered.
However, I'm not sure about any potential performance loss because the content is quite large. Is there a neat and efficient way to render one template with content relevant to a link clicked on a previous page.
what I'm currently doing:
index.html
<div class='well well-sm well-md well-lg'>
<h1> {{variable1}} </h1>
</div>
views.py
def page1(request):
variable1 = 'Long text for each variable. Many variables on each page so
lots of long text overall. '
return render(request, 'index/base.html', {'variable': variable1})
def page2(request):
variable1 = 'More long text. '
return render(request, 'index/base.html', {'variable': variable1})
Basically I want to know if there is a way of doing the above without having to use contexts. Or will I have to create a template for each page?
Check django tutorial doc
In url:
url(r'^(?P<page_id>[0-9]+)/$', views.page, name='page'),
In view:
def page(request, page_id):
// have a dict or list, get result using the page_id
return render(request, 'index/base.html', {'variable': variable})
Or if you don't have a page_id/name system, you can use:
def page(request):
url = request.path
value = dict[url]
render (...)
You need to solve in 2 steps.
Determine previous page, so that you choose appropriate text.
You can solve this in the view. Just set some context variable to be passed to templates. I believe this is not easy to do in template processing, view is better.
Sample code
def page(request):
if previous_page == "some page 1"
page_type = 1
elif previous_page == "some page 2"
page_type = 2
return render(request, 'index/base.html', {'page_type': page_type})
Then in template use {%include%} tag to use appropriate template.
Sample index.html
<div class='well well-sm well-md well-lg'>
{% if page_type == 1 %}
{%include "page_type1.html"%}
{%else%}
{%include "page_type2.html"%}
{%endif
</div>
You need to write 2 templates page_type1.html and page_type2.html for render required html.
After a new article is posted (in a form via Ajax) on my Django news site, I want to return a link to the article.
To do so, I'm using a Template object that says:
if form.is_valid():
form.instance.user = request.user
new_article = form.save()
success = Template('<div id="panel_input" class="col-lg-4"> <h2 class="text-center"> Success </h2> <p class="text-center"> Your Article has been posted. You can see and edit details by clicking here. </p></div>')
context = Context({"article_id" : new_article.pk})
return HttpResponse(success.render(context))
The urlsConf for this looks like:
...
url(r'^article/(?P<article_id>\d+)/$', views.article, name='article'),
...
The problem is that I get an error because of {% url "article_manager:article" %}/{{ article_id }}. Apparently, I must pass the article_id inside the previous tag, since the urlsConf requires the id parameter.
But I also get an error when I put the second tag inside the first, like this:
{% url "article_manager:article" {{ article_id }} %}
I'm not sure how to accomplish this task, it doesn't seem to work with the tools I have. Does anyone have any suggestions?
Try {% url "article_manager:article" article_id=article_id %}
Maybe a little more explanation is needed: You were calling the template tag right {% url "namespace:name" %}. Remember that some templatetags can take arguments, in the *args, **kwargs form. The args can be any simple expression understood by the template language, including a context variable (no need to add double-braces). The kwargs follow the same rule, and have the form argument=expression. Thus, you can call some template tags with the form {% tag "exp" 1 request number=5 username=user.name %}
I am new to the Django web framework.
I have a template that displays the list of all objects. I have all the individual objects listed as a link (object title), clicking on which I want to redirect to another page that shows the object details for that particular object.
I am able to list the objects but not able to forward the object/object id to the next template to display the details.
views.py
def list(request):
listings = listing.objects.all()
return render_to_response('/../templates/listings.html',{'listings':listings})
def detail(request, id):
#listing = listing.objects.filter(owner__vinumber__exact=vinumber)
return render_to_response('/../templates/listing_detail.html')
and templates as:
list.html
{% for listing in object_list %}
<!--<li> {{ listing.title }} </li>-->
{{ listing.title}}<br>
{% endfor %}
detail.html
{{ id }}
The variables that you pass in the dictionary of render_to_response are the variables that end up in the template. So in detail, you need to add something like {'listing': MyModel.objects.get(id=vinumber)}, and then the template should say {{ listing.id }}. But hat'll crash if the ID doesn't exist, so it's better to use get_object_or_404.
Also, your template loops over object_list but the view passes in listings -- one of those must be different than what you said if it's currently working....
Also, you should be using the {% url %} tag and/or get_absolute_url on your models: rather than directly saying href="{{ listing.id }}", say something like href="{% url listing-details listing.id %}", where listing-details is the name of the view in urls.py. Better yet is to add a get_absolute_url function to your model with the permalink decorator; then you can just say href="{{ listing.get_absolute_url }}", which makes it easier to change your URL structure to look nicer or use some attribute other than the database id in it.
You should check the #permalink decorator. It enables you to give your models generated links based on your urls pattern and corresponding view_function.
For example:
# example model
class Example(models.Model):
name = models.CharField("Name", max_length=255, unique=True)
#more model fields here
#the permalink decorator with get_absolute_url function
#models.permalink
def get_absolute_url(self):
return ('example_view', (), {'example_name': self.name})
#example view
def example_view(request, name, template_name):
example = get_object_or_404(Example, name=name)
return render_to_response(template_name, locals(),
context_instance=RequestContext(request))
#example urls config
url(r'^(?P<name>[-\w]+)/$', 'example_view', {'template_name': 'example.html'}, 'example_view')
Now you can do in your templates something like this:
<a href={{ example.get_absolute_url }}>{{ example.name }}</a>
Hope this helps.
In your detail method, just pass the listing into your template like so:
def detail(request, id):
l = listing.objects.get(pk=id)
return render_to_response('/../templates/listing_detail.html', {'listing':l})