How do you add similar posts to a detail page using django - python

I tried to show similar posts to an article in an aside column. A feauture sites like youtube and even stack overflow. Not having anyone to ask about it, I assumed articles listed on the side were ones with similar tags. But it's not working its saying nothing matches. this is what I had in my post_detail.html:
{% block content %}
<div class="row" style="margin-top: 70px">
<div class="col-sm-8">
{% if instance.image %}
<img src='{{ instance.image.url }}' class="img-responsive" />
{% endif %}
<p>Share on:
<a href="https://www.facebook.com/sharer/sharer.php?u={{ request.build_absolute_uri }}">
Facebook
</a>
<a href="https://twitter.com/home?status={{ instance.content | truncatechars:80 | urlify }}%20{{ request.build_absolute_uri }}">
Twitter
</a>
<a href='https://plus.google.com/share?url={{ request.build_absolute_uri }}'></a>
<a href="https://www.linkedin.com/shareArticle?mini=true&url={{ request.build_absolute_uri }}&title={{
instance.title }}&summary={{ share_string }}&source={{ request.build_absolute_uri }}">
Linkedin
</a>
</p>
<h1>{{ title }}<small>{% if instance.draft %}<span style="color:red"> Draft</span>{% endif %} {{instance.publish}}</small></h1>
{% if instance.user.get_full_name %}
<p>By {{ instance.user.get_full_name }}</p>
{% else %}
<p>Author {{ instance.user }}</p>
{% endif %}
<p><a href='{% url "posts:list" %}'>Back</a></p>
<p><a href='{% url "posts:delete" instance.id %}'>delete</a></p>
<p>{{instance.content | linebreaks }}</p>
<hr>
</div>
<div class="panel panel-default pull-right" style="height: 1000px">
<div class="panel-heading">
<h3 class="panel-title">Similar Articles</h3>
</div>
==========right here====================
<div class="panel-body">
{% for tag in instance.tags.all %}
<h4> {{ tag.title }} </h4><hr>
{% endfor %}
</div>
==========right here====================
</div>
</div>
{% endblock content %}
and this is my view
def post_detail(request, slug=None):
instance = get_object_or_404(Post, slug=slug)
if instance.publish > timezone.now().date() or instance.draft:
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
share_string = quote_plus(instance.content)
context = {
"title": "detail",
"instance": instance,
"share_string": share_string,
}
return render(request, "posts/post_detail.html", context)
if this approach is beyond syntax correction and needs to be rewritten. I don't mind writing it over the correct way. This is my second month working with Django. To me this way made sense but it's not working. And are sites like youtube which has a video and similar videos to the right of the main video, are those videos there because they share similar tags? any and all help is welcome.

In the long run and to not reinvent the wheel using a reusable Django application already tried and tested is the sensible approach. In your case there is such app: django-taggit
and is easy to use:
You install it
pip install django-taggit
Add it to your installed apps:
INSTALLED_APPS = [
...
'taggit',
]
Add its custom manager to the model on which you want tags
from django.db import models
from taggit.managers import TaggableManager
class YourModel(models.Model):
# ... fields here
tags = TaggableManager()
and you can use it in your views:
all_tabs = instance.tags.all()
It even has a similar_objects() method which:
Returns a list (not a lazy QuerySet) of other objects tagged similarly
to this one, ordered with most similar first.
EDIT
To retrieve similar posts you should use:
similar_posts = instance.tags.similar_objects()
and to get only the first, let's say, 5 similar posts:
similar_posts = instance.tags.similar_objects()[:5]
where instance is a instance of the Post model.

you should let us know what is not matching.
your post_detail tries to find a Post with a tag's slug.
instance = get_object_or_404(Post, slug=slug)
I doubt that's what you intended.
get_object_or_404 either tries to find an exact match or raise error.
Since your original post has the tag, you will be getting the same post or multiple.
The following block of code is not what you said you wanted either.
{% for tag in instance.tags.all %}
<h4> {{ tag.title }} </h4><hr>
{% endfor %}
It lists all tags of the original post, doesn't list related post (via tag)
If you want to show related post, and you intent to use tag to define relatedness, define a method in your post model to return such related posts.
def get_related_posts_by_tags(self):
return Post.objects.filter(tags__in=self.tags.all())
are those videos there because they share similar tags?
Not sure how they judge the relatedness, you should ask that in a separate question.
If I have to guess, it would be more than just tag comparison though.
** edit
Actually, proper term for relatedness is similarity.
You might find further info by googling document similarity.
{% for post in instance.get_related_post_by_tag %}
// href to post.absolute_url
{% endfor %}

Related

Wagtail - show latest 3 posts but filter the 3 posts when in the latest post selected

bit hard to explain this one but I will give it my best. I am trying to develop a wagtail website and all is looking fine, but now I have come across a problem. In my side panel to the right I have the latest 3 posts published. This always shows the latest 3 posts which is great for further reading but when I select one of those posts in the side panel, the post that I have selected is still visible in the side panel.
So basically I am looking for some kind of restructuring to my models to say if post selected, in side panel show the next latest post applicable instead.
a visual representation is thus:
So my code is as follows:
template:
<div class="col-2__sm">
<div class="card">
{% for child in page.get_recent_blogs %}
<div class="col-1 card__block">
<div class="card__content news-blocks">
<a class="blog__recent-posts" href="{{ child.url }}">
<div class="image">
{% image child.image fill-850x450-c100 %}
</div>
<h3>{{ child.title }}</h3>
<p>{{ child.introduction|truncatewords:15 }}</p>
<span>Read More</span>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
In my model I have this:
def get_recent_blogs(self):
max_count = 3
return BlogPage.objects.all().order_by(
'-first_published_at')[:max_count]
# add this to custom context
def get_context(self, request):
context = super(BlogPage, self).get_context(request)
context['blogs'] = self.get_recent_blogs()
return context
I have tried searching for this but have got no where. I hope I have explained this so you can understand what I am trying to do. I would be very grateful for any help on this
Use Django's exclude method to filter out the current page:
def get_recent_blogs(self):
max_count = 3
return BlogPage.objects.exclude(id=self.id).order_by(
'-first_published_at')[:max_count]

How can I open a page with only one django model object's info after clicking it's ImageField on another page?

I am displaying django models on one of my website's pages. When I press one's ImageField on the page, I want it to open another page including only that one object. How do I do that ?
I thought about using the filter method in my views.py for filtering through my objects and finding that exact one, but I don't know what arguments to use.
Any ideas? (I am a beginner in django)
VIEWS.PY
from django.shortcuts import render
import requests
from . import models
def index(request):
return render(request, 'base.html')
def new_search(request): ********NOT IMPORTANT (I THINK)********
search = request.POST.get('search')
models.Search.objects.create(search=search)
objects = models.Object.objects.all()
results = objects.filter(name__contains=search).all()
args = { 'results': results }
return render(request, "my_app/new_search.html", args)
def individual_page(request):
link = request.GET.get('object-link')
objects = models.Object.objects.all()
return render(request, "my_app/individual_page.html")
MY TEMPLATE
{% extends "base.html" %}
{% block content %}
{% load static %}
<h2 style="text-align: center">{{ search | title }}</h2>
<div class="row">
{% for result in results %}
<div class="col s4">
<div class="card medium">
<div class="card-image">
<a name="object-link" href="{% url 'individual_page' %}"><img src="{{ result.image.url }}" alt=""></a>
</div>
<div class="card-content">
<p>{{result.name}}</p>
</div>
<div class="card-action">
View listing: Price TEST
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
So, the thing I want to do is: when I press the anchor tag that includes the image, I get redirectioned to another page which contains only that one object's info.

Deleting comments from a post: NoReverseMatch while trying to delete comment of a post

I am quite new to django and web development and trying to delete the comment from posts, and has provided a class based view, but I am getting this error while running the code.
Reverse for 'delete_comment' with no arguments not found. 1 pattern(s) tried: ['posts/(?P<slug>[^/]+)/(?P<comment_id>[0-9]+)/delete/$']
I have provided the comment section below and you can find the delete section towards the last.
comment-section.html
{{ comments.count }} Comment{{ comments|pluralize }}
{% for comment in comments %}
<blockquote class="blockquote">
<img style="float:left; clear: left;" class="rounded-circle article-img" height="50" width="50" src="{{ comment.user.profile.profile_pic.url }}"><h6>{{ comment.user.first_name|capfirst }} {{ comment.user.last_name|capfirst }}</h6><br>
<p style="font-size: 8px;">{{ comment.timestamp }}</p>
<p style="font-size: 14px;" class="mb-3">{{ comment.content }}</p>
<a type="button" name="button" class="reply-btn ml-4"><p style="font-size: 13px;"> Reply</p></a>
{% if request.user == comment.user %}
Delete</td>
{% endif %}
views.py
class DeleteCommentView(BSModalDeleteView, LoginRequiredMixin, UserPassesTestMixin):
model = Comment
template_name = 'posts/comment_delete.html'
success_message = 'Deleted'
def get_success_url(self):
return reverse_lazy('posts:detail_post')
def test_func(self, comment_id):
comment = self.get_object(self.comment_id)
if self.request.user == comment.user:
return True
return False
urls.py
path('<slug>/<int:comment_id>/delete/', DeleteCommentView.as_view(), name='delete_comment'),
Please do let me know how can I let users delete their comments from the post. It was easier to delete the whole post.
Thanks in advance!
It looks like your URL takes two arguments, slug and comment_id, but you are only passing in comment_id when you use the {% url %} tag.
It's not altogether obvious what the slug is since you don't show the model, but assuming it's the post slug, you could perhaps fix this with:
{% url 'posts:delete_comment' comment.post.slug comment.id %}
Probably the better solution however is to have your URL not take the slug at all since the ID is enough to identify it. We can't see the code for BSModalDeleteView but I'm guessing it is only expecting an ID, in which case you might need a new URL just for deleting comments such as:
path(
"/comments/<int:pk>/delete/",
DeleteCommentView.as_view(),
name="delete_comment",
)

URLs being produced correctly but not directing to correct template

I'm trying to follow the Simple Blog tutorial at Django By Example. I've managed to get as far as producing a site that loads correctly, but while the index view is loading find, and the links to the individual posts show up and appear to be formatted correctly, they point back to the index template so all that happens when you click on them is that it reloads the index view. I'm new to Django and the tutorial is sparse to say the least and not helped by the fact it's written for an old version of Django and I'm using 1.5. I've been staring at it all afternoon and I'm pretty lost.
Here's my urls.py
from django.conf.urls import patterns, url
from blog import views
urlpatterns = patterns('blog.views',
#index
(r"$", 'main'),
#ex: /1/
(r"^(\d+)/$", 'post'),
#ex: /add_comment/1/
(r"^add_comment/(\d+)/$", 'add_comment'),
)
my views.py
from blog.models import Post, PostAdmin, Comment, CommentAdmin
class CommentForm(ModelForm):
class Meta:
model = Comment
exclude = ["post"]
def main (request):
"""Main Listing."""
posts = Post.objects.all().order_by("-created")
paginator = Paginator(posts, 10)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
posts = paginator.page(page)
except (InvalidPage, EmptyPage):
posts = patinator.page(paginator.num_pages)
return render_to_response("blog/list.html", dict(posts=posts, user=request.user))
def post (request, pk):
"""single post with comments and comment form"""
post = Post.objects.get(pk=int(pk))
comments = Comment.objects.filter(post=post)
d = dict(post=post, comments=comments, form=CommentForm(), user=request.user)
d.update(csrf(request))
return render_to_response("blog/post.html", d)
and the list.html that contains the links that aren't going anywhere!
{% extends "blog/bbase.html" %}
{% block content %}
<div class="main">
<!-- Posts -->
<ul>
{% for post in posts.object_list %}
<div class="title">{{ post.title }}</div>
<ul>
<div class="time">{{ post.created }}</div>
<div class="body">{{ post.body|linebreaks }}</div>
<div class="commentlink">Comments</div>
</ul>
{% endfor %}
</ul>
<!-- Next/Prev page links -->
{% if posts.object_list and posts.paginator.num_pages > 1 %}
<div class="pagination" style="margin-top: 20px; margin-left: -20px; ">
<span class="step-links">
{% if posts.has_previous %}
newer entries <<
{% endif %}
<span class="current">
Page {{ posts.number }} of {{ posts.paginator.num_pages }}
</span>
{% if posts.has_next %}
>> older entries
{% endif %}
</span>
</div>
{% endif %}
</div>
{% endblock %}
The Django URL resolver will return the first URL pattern that matches the incoming request. The regex for your 'main' view r"$" will match ANY incoming request since you are only looking for $ which is an end of string character.
You need to alter your 'main' URL regex to be r'^$'.
Alternatively, if you would like a catch-all view, you could move the 'main' view to the bottom of your URLs

Filtering a django news post

I'm new to Django and I have a news post and on that same template I have a section on the right hand side displaying all of the latest posts. However when you are on one of the main news posts it also shows up in the 'latest news' tab on the right.
I'm pretty sure i need to use .exclude to filter out the one that is being displayed. However i don't know how django know which post is being displayed.
If you need to look at my code please ask. I'm only using basic models / views to output the data.
The line that shows the latest 3 posts:
other_news = NewsPost.objects.filter(live=True, categories__in=post.categories.all).distinct().order_by("-posted")[:3]
Code for the template:
<div class='related_article_wrapper'>
{% if other_news %}
{% for news in other_news %}
<div class="article_snipppet_wrap">
<img class="article_icon" src="/media/images/article_icon.png" alt="" />
<p>{{news.title}}</p>
<span>{{news.posted|date:"d/m/y"}} »</span>
</div>
{% endfor %}
<span><a style="text-decoration: none; href="/news-hub/news/">View all news »</a></span>
{% endif %}
</div>
Thanks,
Josh
Just add .exclude(id=post.id) to your filter chain:
other_news = NewsPost.objects.exclude(id=post.id).filter(live=True,
categories__in=post.categories.all).distinct().order_by("-posted")[:3]
exclude() takes arguments in the same format as filter(), it just does the opposite!

Categories