Filtering a django news post - python

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!

Related

How to pass variable from Python API to Django template

I have been trying to work with this Python sports data api, Sportsipy. It’s pretty simple to set up and I can save data to a var and print it to the terminal but when I add it to context go to pass it through my Django template nothing shows up.
I tried calling it on the HTML side several different ways but I still haven't been able to figure it out.
Screenshot of API endpoint doc
Under the Schedule endpoint
https://sportsreference.readthedocs.io/en/stable/ncaab.html#module-sportsipy.ncaab.boxscore
def games(request):
""" View to return games page """
team_schedule = Schedule('PURDUE')
print(team_schedule)
for game in team_schedule:
away_total_rebounds = game.boxscore.away_total_rebounds
print(away_total_rebounds)
context = {
'team_schedule': team_schedule,
'away_total_rebounds': away_total_rebounds,
}
return render(request, 'games/games.html', context)
{% for game in team_schedule %}
<div>
<h4 class="white">{{ game.boxscore.away_total_rebounds }}</h4>
</div>
{% endfor %}
I'm not sure if it is the correct answer but shouldn't your for loop have elements of the for loop? What I'm trying to say is in the template shouldn't it be like
{% for game in team_schedule %}
<div>
<h4 class="white">{{ game.boxscore.away_total_rebounds }}</h4>
</div>
{% endfor %}
EDIT: Abdul's answer added

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]

Difficulty with pagination and row formatting in Django template

I'm trying to implement pagination. I'm mainly following django documentation for pagination,https://docs.djangoproject.com/en/1.8/topics/pagination/...I'm not sure what i did wrong but the pagination effect is not being activated: When I set page to only have three posts, it still shows nine posts. I didn't do anything special, I just followed the documentation.
def category_detail(request, slug):
obj = NewsCategory.objects.get(slug=slug)
newsInCat = obj.news_set.all() #for the list of news
paginator = Paginator(newsInCat, 3) # Show 25 contacts per page
page = request.GET.get('page')
try:
news_set = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
news_set = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
news_set = paginator.page(paginator.num_pages)
bestInCat = obj.news_set.get_bestInCat()
specialInCat = obj.news_set.get_special()
mustSeeInCat = obj.news_set.get_mustSeeInCat()
recommend = obj.news_set.get_recommend()
ad2 = Sponsored.objects.get_ad2()
context = {
"obj":obj,
"news_set":news_set,
"newsInCat":newsInCat,
"bestInCat":bestInCat,
"specialInCat":specialInCat,
"mustSeeInCat":mustSeeInCat,
"recommend":recommend,
"ad2":ad2
}
and the below is my html...beside pagination, I'm having one more issue. When the title of the post becomes too long that it breaks another line, the format of my page gets messed up. It looks like this
<div class="row">
<article>
{% for news in newsInCat %}
<div class='col-sm-4'>
<div class="content">
<figure class="story-image">
</figure>
<div id="forever "style="margin-bottom:30px;">
<a href='{{news.get_absolute_url }}' style="text-decoration:none; color:#282E5C;"><h4 style="font-size: 18px;
font-weight: 400;">{{news.title}}</h4></a>
</div>
</div>
</div>
{% endfor %}
</article>
</div>
<div class="pagination">
<span class="step-links">
<!-- {% if news_set.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ news_set.number }} of {{ news_set.paginator.num_pages }}.
</span> -->
{% if news_set.has_next %}
Load More
{% endif %}
</span>
</div>
There are two separate problems at work here which have been indicated in the above answers:
You are not using Bootstrap correctly: although you can append multiple <div class="col-sm-4"> together, you will see the irregular collapsing behavior in your screenshot if they are different heights. The purpose of <div class="row"> is to ensure that your columns will appear in separate rows. See Must Bootstrap container elements include row elements? for more information.
You can resolve this with code like the following in your for-loop to add a new row every third item:
{% if forloop.counter|divisibleby:3 %}
</div>
<div class="row">
{% endif %}
You are not using the correct context object in your template: The paginator object is passed as news_set in your context object but the template uses another context object: newsInCat, which is not paginated. If you follow #Sayse's suggestion of using the news_set object, you should be in good shape:
{% for news in news_set %}
As a final suggestion, the <article> tag does not seem to be doing anything besides giving semantic value. Why not just use it instead of the div, so that you have <article class="col-sm-4">?
And as a final note, camelCase is generally frowned on in Python. Try using_underscores_with_lowercase, like you've already done with news_set.
Adding all these suggestions, you would only need to amend your template to something like this:
<div class="row">
{% for news in news_set %}
<article class="col-sm-4">
<!-- add your article content here...and clean it up! You have unnecessary spaces, inconsistent use of single and double quotes, and inline styles that (probably) should be defined in an external stylesheet. -->
</article>
{% if forloop.counter|divisibleby:3 %}
</div>
<div class="row">
{% endif %}
{% endfor %}
</div>
I can speak to your HTML/CSS formatting problem. col-sm-4 tells me you're using Bootstrap, which defines layouts in terms of 12 column width. Bootstrap will always attempt to make the with of all rows equal 12.
Right now, you're looping over all the objects and adding n columns each of width col-sm-4. Booststrap is trying to make sure each takes up a third of the row, but you're adding more that 3 divs, which is more than 12 total column width. Once the column is full, (starting with the 4th news item), Bootstrap moves the divs as closely as it can to the top of the row while still obeying the rule that width can only be 12.
In the case you pictured, Bootstrap offsets the 4th and 5th of divs because the offset allows them to be closer to the top of the row.
To fix this, you'd need to have each set of three news items in it's own row, so that your 3 col-sm-4 divs total a width of 12.
Judging based off the template, you must be still including the full list as well as the paginated set
In your template you are iterating over newsInCat instead of news_set
{% for news in newsInCat %}
should be
{% for news in news_set %}

Flask & Bootstrap Multiple collapsibles, but they each only open the first

I have an HTML page displaying a database populated by emails. I have them displayed in a collapsible, and for each post the timestamp of it is what toggles it and the innards are the email itself. The HTML page is structured like this:
{% extends "base.html" %}
{% block content %}
{% for email in emails %}
<div><button class="btn" data-toggle="collapse" data-target="#demo">{{ email.timestamp }}</button>
<div id="demo" class="collapse">
{{ email.body }}
</div>
{% endfor %}
{% endblock %}
relevant portion of views.py
#app.route('/cruz')
def cruz():
u = Politician.query.get(1)
emails = u.emails.all()
return render_template('cruz.html',title='Ted Cruz',emails=emails)
which produces a webpage that looks like this: http://imgur.com/noqC40E
The problem is that no matter which of those timestamps I click, only the first collapsible opens and closes. I've tried a number of things to fix it, mostly messing around with the HTML page and the for blocks and where I place the {{ email.body }}, but nothing I do seems to work. Can anyone see where this is going wrong?
You are generating the same id attribute for your div each time:
<div id="demo" class="collapse">
You almost certainly need to generate unique ids. You could generate unique ids by adding the loop index perhaps:
<div id="demo-{{loop.index}}" class="collapse">

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

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 %}

Categories