Django - Exclude current post from side bar - python

I am playing with Django but I'm having trouble with my templates. In my post detail in including a recent posts side bar from a template tag but I want to exclude the current post if it is, in fact, one of the most recent. My original hope was to use .exclude(id__post_detail=post) in the blog_tag but I think I might be missing an important concept, do I need to request post_detail return its response and then I can do that? Or perhaps define the query in the view and then call it to the blog_tag?
Many thanks in advance.
1.blog_tags.py
from django import template
register = template.Library()
from django.db import models
from django.utils import timezone
from ..models import Post
#register.inclusion_tag('blog/sidebar.html')
def sidebar_latest(request, count=5):
latest_posts= Post.objects.filter(published_date__lte=timezone.now()).order_by('-published_date')[:count]
return {'latest_posts': latest_posts}
views
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
post_detail
{% extends 'blog/base.html' %}
{% load blog_tags %}
{% block content %}
<div class="col-sm-12 col-md-9">
<div class="post">
<h1>{{ post.title }}</h1>
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<p>{{ post.text|linebreaksbr }}</p>
</div>
</div>
<div class="col-sm-12 col-md-3">
{% sidebar_latest 3 %}
</div>
{% endblock %}

Related

ValueError at /feed/post/5/comment/ The view feed.views.add_comment_to_post didn't return an HttpResponse object. It returned None instead

I am using this exact code in another project, the only difference is that I have changed the model names, urls patterns, and template names since it is a different project. However, I am getting this error and I have no idea why. I am trying to bring the user to a detail page that has both the post and any comments on the post, as well as a link that takes to a page that allows the user to add a comment to the post.
app Views.py:
#login_required
def add_comment_to_post(request,pk):
post = get_object_or_404(UserPost,pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('feed:post_detail', pk=userpost.pk)
else:
form = CommentForm()
return render(request,'feed/comment_form.html',{'form':form})
userpost_detail.html (post_detail in urls)
{% extends 'base.html' %}
{% block content %}
<h1 class="posttitle">{{ userpost.title }}</h1>
<p class="postcontent">{{ userpost.post_body }}</p>
{% if request.user.is_authenticated and request.user == post.author %}
<a class="link" href="{% url 'feed:edit_post' post.id %}">Edit Post</a>
{% endif %}
<hr>
Add Comment
<div class="container">
{% for comment in post.comments.all %}
<br>
{% if user.is_authenticated or comment.approved_comment %}
{{ comment.create_date }}
<a class="btn btn-warning" href="{% url 'comment_remove' pk=comment.pk %}">
<span class="glyphicon glyphicon-remove"></span>
</a>
<p>{{ comment.comment_body }}</p>
<p>Posted By: {{ comment.author }}</p>
{% endif %}
{% empty %}
<p>No Comments</p>
{% endfor %}
</div>
{% endblock %}
comment_form.html:
{% extends 'base.html' %}
{% block content %}
<h1>New Comment</h1>
<form class="post-form" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Add Comment</button>
</form>
{% endblock %}
Traceback and error:
Traceback (most recent call last):
File "/anaconda/envs/test/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/anaconda/envs/test/lib/python3.6/site-packages/django/core/handlers/base.py", line 198, in _get_response
"returned None instead." % (callback.__module__, view_name)
ValueError: The view feed.views.add_comment_to_post didn't return an HttpResponse object. It returned None instead.
Your view does not return anything if the request is not a POST.
The problem is one of indentation. The last three lines - from else onwards - need to be moved back one level.

Wrong posts sequence at home page [Django app]

At my home-page I've got exhibited all posts on my blog, but they're sorted incorrectly, from the oldest post to newest(it has to be reversed).
I use querysets to sort posts order by published date in my views.py
def home(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, "home.html", {'posts': posts})
And that's my home.html source code:
{% extends "C:\myapp\blog\templates\base.html" %}
{% block content %}
{% for post in posts %}
<div class="post">
<div class="date">
{{ post.published_date }}
</div>
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
{% endblock content %}
Could you help me in reverse these posts?
Thanks in advance.
You want to add a - to the string argument in order_by this will cause your queryset to be in descending order.
def home(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('-published_date')
return render(request, "home.html", {'posts': posts})
Notice the .order_by('-published_date')

I do not understand this Django error

Error during template rendering
In template
C:\Users\Paddy\Desktop\Django-tut\mysite\blog\templates\blog\post_list.html,
error at line 10 Reverse for 'post_detail' with arguments '()' and
keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried:
['blog/post/(?P[0-9]+)/$']
{% extends 'blog/base.html' %}
{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<h1>{{ post.title }}</h1>
<!--<h1>{{ post.title }}</h1>-->
<p>{{ post.text|linebreaks }}</p>
</div>
{% endblock %}
My post_detail.html file looks like this
{% extends 'blog/base.html' %}
{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaks }}</p>
</div>
{% endblock %}
My urls.py is
from django.conf.urls import include, url
from . import views
urlpatterns = [
url(r'^$', views.post_list, name='post_list'),
url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
]
and views.py is
from django.shortcuts import render
from django.utils import timezone
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
#Post.objects.get(pk=pk)
# Create your views here.
def post_list(request):
return render(request, 'blog/post_list.html', {})
Thanks.
Assuming the first template you've posted is post_list.html you don't send any context variables to it.
In post_list view - if you want to list all posts - you have to add:
def post_list(request):
posts = Post.objects.all()
return render(request, 'blog/post_list.html', {'posts': posts})
Then in your post_list.html template you have to loop over posts:
{% extends 'blog/base.html' %}
{% block content %}
{% for post in posts %} # iterate over posts
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaks }}</p>
</div>
{% endfor %}
{% endblock %}
There error message complains that it can't find a reverse URL when pk is '', the empty string.
Indeed there is no URL that matches that, as the regular expression required [0-9]+, and the empty string doesn't match that. So a reverse match can't be found.
The reason why pk was empty is explained in the other answer.

Django redirect back to previous page on form post

I'm trying to have a form post redirect back to the page it was posted from. So my app has a detail page that displays information about a place, /arena/123, and on the same page there is a form that a user can enter some comments or ratings about the current place they are looking at. These are my view functions.
class DetailView(LoginRequiredMixin, generic.DetailView):
model = Arena
template_name = 'arenas/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
if 'form' not in context:
print 'no form in context'
context['form'] = RatingForm
return context
def rate(request):
if request.method == 'POST':
form = RatingForm(request.POST)
if form.is_valid():
# post was valid. go back to page and display data
return DetailView.as_view()(request(), form=RatingForm)
else:
# post was unsuccessful. go back to the page and display errors
print form.errors
return DetailView.as_view()(request(), form=form)
and my template
{% extends "base.html" %}
{% block content %}
<h1>{{ arena.address }}</h1>
<h1>{{ arena.address_2 }}</h1>
<h1>{{ arena.phone }}</h1>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<form action="/arenas/rate/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
<div>
{{comments}}
</div>
{% endblock %}
So what I'd like to do is after a user submits the form on the page I'd like to go back to the same detail page I was on and either have the data that user posted there shown in the comments section if it was successful or redirect back to the same page but with the form errors if it was unsuccessful. My problem is right now I don't know how to retain the url and form errors to display the detail page again. I don't think I can use return DetailView.as_view()(context(), form=form) because my DetailView doesn't have a post so I get a 405. Every example I see doesn't cover redirecting back to the same page. They usually just show a HttpResponseRedirect(/thanks) or something similar.
Also note. I don't want to post to /arena/123 because eventually that will be another feature to update information about the place so I didn't want to put the comments post on that url.
So I dug into some more Django examples and stumbled across SingleObjectMixin. This combined with a FormView seems to be what I want. The Django docs had an example that was 99.9 percent what I wanted here. Just be sure to read down to the better solution section.
I changed my views to this
class DetailView(LoginRequiredMixin, generic.DetailView):
model = Arena
template_name = 'arenas/detail.html'
def get_context_data(self, **kwargs):
print kwargs
context = super(DetailView, self).get_context_data(**kwargs)
context['form'] = RatingForm
return context
class RatingView(LoginRequiredMixin, detail.SingleObjectMixin, generic.FormView):
model = Arena
template_name = 'arenas/detail.html'
form_class = RatingForm
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(RatingView, self).post(request, *args, **kwargs)
def get_success_url(self):
print "successfully posted"
return reverse('arenas:detail', kwargs={'pk': self.object.pk})
Added a route for posting the form with /rate
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$',
views.DetailView.as_view(), name='detail'),
url(r'^(?P<pk>\d+)/rate/$', views.RatingView.as_view(), name='rate')
)
and modified my template a bit to pass the id of the object to my route in the form post action
{% extends "base.html" %}
{% block content %}
<h1>{{ arena.address }}</h1>
<h1>{{ arena.address_2 }}</h1>
<h1>{{ arena.phone }}</h1>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<form action="/arenas/{{ arena.id }}/rate/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
<div>
{{comments}}
</div>
{% endblock %}
Now on an error the context is kept and the SingleObjectMixin/FormView combination keeps me on the same page to display the form errors and on a successful post it redirects to the detail page I want using the get_success_url to load the page again with the new information that was posted.

Django dynamic homepage

I have a lots of apps, but I want to generate all app's data on my homepage. Here's my apps:blog,members
I want to show blogs and my members on my homepage
I know that if I want to generate one app's data,I can do this:
blog/urls.py:
urlpatterns = [
url(r'^$', ListView.as_view(
queryset=Post.objects.all().order_by("-date")[:10],
template_name='blog1.html')),
and in blog1.html:
{% extends "index.html" %}
{% block blog %}
{% for post in blog_post %}
<div id="blog-wrapper" class="bgrid-third s-bgrid-half mob-bgrid-whole group">
<article class="bgrid">
<h5>{{ post.date }} </h5>
<h3><div class = "entry-title">{{ post.title }}</div></h3>
<p>{{ post.user }}</p>
<p><div class = "post_body">{{ post.body|safe|linebreaks }}</div></p>
</article>
</div>
{% endfor %}
{% endblock %}
{% block member %}
Here when I go to the url,I can see all blogs I write,but now I want to see blogs and members(another app) on one page, so how can I do this?
I would suggest you to use ListView's get_context_data method.
Create view in your project's views.py:
from django.views.generic import ListView
# Import your models here.
class HomepageView(ListView):
model = Post
ordering = '-date'
template_name = 'blog1.html'
context_object_name = 'posts'
def get_context_data(self, **kwargs):
context = super(HomepageView, self).get_context_data(**kwargs)
context['members'] = Member.objects.all()
return context
def get_queryset(self):
return super(HomepageView, self).get_queryset()[:10]
Then, change urls.py:
from django.conf.urls import url
# Import ``HomepageView`` here.
urlpatterns = [
url(r'^$', HomepageView.as_view(), name='homepage'),
# Other patterns here.
]
Now you can access posts using posts variable and members using members variable.
from .models import Post
from member.models import UserProfile
def get_data():
return {
"post": Post.objects.all().order_by("-date")[:10],
"user": UserProfile.objects.all()[:10]
}
then in my blog1.html:
{% extends "index.html" %}
{% block blog %}
{% for post in object_list.post %}
<div id="blog-wrapper" class="bgrid-third s-bgrid-half mob-bgrid-whole group">
<article class="bgrid">
<h5>{{ post.date }} </h5>
<h3><div class = "entry-title">{{ post.title }}</div></h3>
<p>{{ post.user }}</p>
<p><div class = "post_body">{{ post.body|safe|linebreaks }}</div></p>
</article>
</div>
{% endfor %}
{% endblock %}
{% block member %}
{% for post in object_list.user %}
<div class="bgrid member">
<div class="member-header">
<div class="member-pic">
<img src="{{ post.portrait }}" alt=""/>
</div>
<div class="member-name">
<h3>{{ post.user }}</h3>
<span>Creative Director</span>
</div>
</div>
<p>{{ post.intro }}</p>
<ul class="member-social">
<li><i class="fa fa-google-plus"></i></li>
<li><i class="fa fa-github"></i></li>
</ul>
</div> <!-- /member -->
{% endfor %}
{% endblock %}

Categories