How can I order post entries in a Django template - python

I made my post model where I store all the post info, like the title, the body, the tags an so on. And I linked it to my index template with a for loop:
<!-- Blog Post -->
{% for blog in object_list %}
<div class="card mb-4">
<div class="card-body">
<h4 id="post_title" class="card-title">{{blog.title}}</h2>
<div id="post_body" class="card-text">{{blog.formatted_markdown|safe}}</div>
Read More
</div>
<div class="card-footer text-muted">
<p id="foot_page">{{blog.date}}</p>
<a id="foot_page" href="#">{{blog.author}}</a>
</div>
</div>
{% endfor %}
And when I run my server the post appear on the blog ordered by pub_date ascending, so the new ones appear delow the old ones.
What can I do to make the newest post appear on top?
Here's my Post model class if it's useful:
class Post(models.Model):
title = models.CharField(max_length=255)
body = MarkdownxField()
tags = models.ManyToManyField(Tag)
author = models.CharField(max_length=255, default='Raúl Chirinos')
date = models.DateTimeField(auto_now_add=True, blank=True)
#property
def formatted_markdown(self):
return markdownify(self.body)
def __str__(self):
return self.title
I managed the views within the urls file:
from django.conf.urls import url
from django.urls import include, path
from django.contrib import admin
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from raulchirinos.models import Post
app_name = 'raulchirinos'
urlpatterns = [
url(r'^markdownx/', include('markdownx.urls')),
url(r'^admin/', admin.site.urls, name='admin-site'),
url(r'^$', ListView.as_view(model=Post, template_name='raulchirinos/index.html'), name='index'),
url(r'^details/(?P<pk>[0-9]+)/$', DetailView.as_view(model=Post, template_name='raulchirinos/post_template/post.html'), name='post_details'),
]

Add a meta class to the models and set the sort according to your requirement.
Ex:
class Post(models.Model):
title = models.CharField(max_length=255)
body = MarkdownxField()
tags = models.ManyToManyField(Tag)
author = models.CharField(max_length=255, default='Raúl Chirinos')
date = models.DateTimeField(auto_now_add=True, blank=True)
class Meta:
ordering = ['-date']
#property
def formatted_markdown(self):
return markdownify(self.body)
def __str__(self):
return self.title

You have several options depending on the level where you need the ordering to happen:
Set a Meta subclass to your Post model with the ordering attribute set to order by date descending. See: https://docs.djangoproject.com/en/2.0/ref/models/options/#ordering
class Post(models.Model):
# ...
class Meta:
ordering = ["-date"]
Order the data from the modle in your view before passing it onto the context.
Reverse the for loop in the template.
Of course the best option is probably the first one, since it's likely that you want this ordering in any case when you retrieve multiple Posts.

Related

How to return single object in a Django model and view

I would like to return a very basic, single paragraph from a model but I don't know how or which is the best approach. It's a simple description textField (maindescription) that I would like to be able to change in the future instead of hard-coding it. It has to be simple way but I just could find a good example. Would appreciate any help on how to properly write the view and retrieve it in the template.
model.py
from autoslug import AutoSlugField
from model_utils.models import TimeStampedModel
from time import strftime, gmtime
# Receive the pre_delete signal and delete the file associated with the model instance.
from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver
class Song(models.Model):
author = models.CharField("Author", max_length=255)
song_name = models.CharField("Song Name", max_length=255)
slug = AutoSlugField("Soundtrack", unique=True, always_update=False, populate_from="song_name")
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
audio_file = models.FileField(upload_to='mp3s/', blank=True)
def __str__(self):
return self.song_name
class MainDescription(models.Model):
main_description = models.TextField()
slug = AutoSlugField("Main Description", unique=True, always_update=False, populate_from="main_description")
def __str__(self):
return self.main_description
view.py
from django.views.generic import ListView, DetailView
from .models import Song, MainDescription
class SongListView(ListView):
model = Song
# Overwrite the default get_context_data function
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add extra information here, like the first MainDescription Object
context['main_description'] = MainDescription.objects.first()
return context
admin.py
from django.contrib import admin
from .models import Song, MainDescription
admin.site.register(Song)
admin.site.register(MainDescription)
urls.py
from django.urls import path
from . import views
app_name = "music"
urlpatterns = [
path(
route='',
view=views.SongListView.as_view(),
name='list'
),
]
song_list.html
{% extends 'base.html' %}
{% block content %}
<div class="container">
<p>{{ main_description.main_description }}</p>
</div>
<ul class="playlist show" id="playlist">
{% for song in song_list %}
<li audioURL="{{ song.audio_file.url }}" artist="{{ song.author }}"> {{ song.song_name }}</li>
{% endfor %}
</ul>
{% endblock content %}
</div>
It looks like you're trying to add extra context to the SongListView
class SongListView(ListView):
model = Song
# Overwrite the default get_context_data function
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add extra information here, like the first MainDescription Object
context['main_description'] = MainDescription.objects.first()
return context
Then in your template you could do something like this
<p>{{ main_description.main_description }}</p>
For more information about that from the docs, you can find it here

Model data not displayed on template - django

Short Story: I have made two apps. Properties and Tenants within a django project. First I started rendering data from Property model to property_detail.html template and it works fine, but after I created & migrated the Tenants model, and I try to render data from there to property_detail.html it doesn't work. Yet it doesn't give me any errors. It just doesn't show up.
Models.py
import arrow
import uuid
from django.db import models
from django_countries.fields import CountryField
from django.urls import reverse
from django.conf import settings
from properties.models import Property
class Tenant(models.Model):
id = models.UUIDField( # new
primary_key=True,
default=uuid.uuid4,
editable=False)
full_name = models.CharField("Full Name", max_length=255, null=True)
email = models.EmailField(unique=True, null=True)
phone = models.CharField(max_length=20, unique=True, null=True)
description = models.TextField("Description", blank=True)
country_of_origin = CountryField("Country of Origin", blank=True)
creator = models.ForeignKey(
settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
created_on = models.DateTimeField(
"Created on", auto_now_add=True, null=True)
is_active = models.BooleanField(default=False)
apartment = models.ForeignKey(
Property,
on_delete=models.CASCADE,
related_name='reviews',
)
rent_tenant = models.CharField(
"Rent he/she pays", max_length=10, blank=True)
def __str__(self):
return self.full_name
def get_absolute_url(self):
""""Return absolute URL to the Contact Detail page."""
return reverse('tenant_detail', kwargs={'pk': str(self.pk)})
urls.py
from django.urls import path
from .views import TenantListView, TenantDetailView
urlpatterns = [
path('', TenantListView.as_view(), name='tenant_list'),
path('<uuid:pk>', TenantDetailView.as_view(), name='tenant_detail'), # new
]
views.py
from django.views.generic import ListView, DetailView
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin # new
from .models import Tenant
class TenantListView(LoginRequiredMixin, ListView): # new
model = Tenant
context_object_name = 'tenant_list'
template_name = 'tenants/tenant_list.html'
login_url = 'account_login' # new
class TenantDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # new
model = Tenant
context_object_name = 'tenant'
template_name = 'tenants/tenant_detail.html'
login_url = 'account_login' # new
permission_required = 'books.special_status' # new
and here is the html template section where I need it to be rendered.
<li class="list-group-item">
{% if tenant.full_name %}
<b>Layout</b> <a class="float-right">{{ tenant.full_name }}</a>
{% endif %}
</li>
<li class="list-group-item">
{% if property.usable_sqm %}
<b>SQM</b> <a class="float-right">{{ property.usable_sqm }}</a>
{% endif %}
</li>
The other app is EXACTLY the same. Basically I copy-pasted everything from there and then just changed the fileds and renamed all the fields from Property to Tenant (By that I mean all the functions and urls ... ) What seems to be the problem? Because by my logic this should work.
The views.py document you have provided doesn’t have property_details.html template, instead it has tenant templates( you trying to render tenant objects into property template right?). I am not sure how you trying passing tenant model to property template from the code provided.
Why don’t you import tenant model into property views and pass whatever tenant objects you want to the property template?

Filter posts based on category

I'm new in django and generally in programming. And now I write my first project - that will be my personal blog, it's almost done except one feature:
I have list of categories shown on the page in the right panel.
1) The question is how can I sort/filter my posts by these categories? I mean I want to click on one of these categories in the right panel and to see the posts where that category is mentioned (post may have several categories).
I've tried a lot of combinations, which found on stackoverflow, but I didn't do that before so can't understand how to realize that feature in my project.
models.py
class Category(models.Model):
title = models.CharField(max_length=20)
def __str__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=100)
overview = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
content = HTMLField('Content')
author = models.ForeignKey(Author, on_delete=models.CASCADE)
thumbnail = models.ImageField()
categories = models.ManyToManyField(Category)
featured = models.BooleanField()
previous_post = models.ForeignKey('self', related_name='previous', on_delete=models.SET_NULL, blank=True, null=True)
next_post = models.ForeignKey('self', related_name='next', on_delete=models.SET_NULL, blank=True, null=True)
def __str__(self):
return self.title
views.py
def filter_by_category(request):
post_list = Post.objects.filter(categories=)
context = {
'post_list': post_list
}
return render(request, 'post_by_category.html', context)
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', index),
path('blog/', blog, name='post-list'),
path('search/', search, name='search'),
path('blog/filter_by_category/', filter_by_category, name='filter_by_category'),
path('subscribe/', email_list_signup, name='subscribe'),
path('create/', post_create, name='post-create'),
path('post/<id>/', post, name='post-detail'),
path('post/<id>/update/', post_update, name='post-update'),
path('post/<id>/delete/', post_delete, name='post-delete'),
path('tinymce/', include('tinymce.urls')),
path('accounts/', include('allauth.urls')),
sidebar.html
<div class="widget categories">
<header>
<h3 class="h6">Categories</h3>
</header>
{% for cat in category_count %}
<div class="item d-flex justify-content-between">
{{ cat.categories__title }}<span>{{ cat.categories__title__count }}</span></div>
{% endfor %}
</div>
Given code doesn't work, I know that the problem in views.py. I'm absolutely messed up how to write it correctly.
Help me please to solve this problem.
UPDATED:
It's solved, thanks a lot to Landcross.
I was close in some moments to that decision, but messed up. Thank you!
First, expand your url definition like so:
path('blog/filter_by_category/<str:category>', filter_by_category, name='filter_by_category'),
Now you are defining a variable named 'category' (which is a string, hence the str) in the url which can be passed to the view:
def filter_by_category(request, category):
post_list = Post.objects.filter(categories__title=category)
context = {
'post_list': post_list
}
return render(request, 'post_by_category.html', context)
And change your template so it adds the parameter to the url:
<div class="widget categories">
<header>
<h3 class="h6">Categories</h3>
</header>
{% for cat in category_count %}
<div class="item d-flex justify-content-between">
{{ cat.categories__title }}<span>{{ cat.categories__title__count }}</span></div>
{% endfor %}
</div>
You can also change the url to int and change your template to use the category ID instead of title if that's something you like more or works better for you (e.g. if category titles can contain duplicates).

Retrieving metadata from a post, then sorting posts by said metadata in django

I have two interconnected models in my blog app; Category and Post. The blog front page displays a list of posts and their corresponding metadata, like it should; fairly standard stuff.
Aside from displaying the posts on the front page, they're also displayed on the individual user's profile page in short form (just the category and the headline).
What I'm interested in doing is sorting all the posts that belong in a category, however the only way I've managed to make it work is something like this:
NEWS
some title
NEWS
another title
PYTHON
another arbitrary title
NEWS
yet another title
I'd like to sort it thusly instead:
NEWS
some title
another title
yet another title
PYTHON
another arbitrary title
Alas, my mind keeps turning into a bowl of spaghetti when I try to come up with a method, so without further ado; how should I go about this bit?
I reckon that there's something off with calling the category from the post's metadata only to try and categorize the posts via the retrieved data, but aside from that, I'm somewhat lost.
Here's the template snippet from user_profile.html:
{% if user.post_set.exists %}
<p>
{% for post in user.post_set.all|dictsortreversed:"date_posted" %}
<span style="margin-right: 5px; padding: 3px 6px; border-radius:12px; color:#FFF; background-color:#FFA826;">{{ post.category }}</span><br/>
<a style="margin-left:3px;" href="{% url 'blog:post-detail' post.slug %}">{{ post.title|truncatechars_html:30 }}</a><br/>
{% endfor %}
</p>
{% endif %}
The models:
class Category(models.Model):
class Meta:
verbose_name = 'category'
verbose_name_plural = 'categories'
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=60)
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.CASCADE)
content = RichTextUploadingField(
external_plugin_resources=[(
'youtube',
'/static/ckeditor/ckeditor/plugins/youtube/',
'plugin.js'
)],
blank=True,
null=True,
)
date_posted = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=70, blank=True, null=True, help_text='<font color="red">don\'t. touch. the. slug. field. unless. you. mean. it.</font> (it will auto-generate, don\'t worry.)')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post-detail', kwargs={'slug': self.slug})
And finally the view which relate to the post_list.html:
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = '-date_posted'
paginate_by = 6
Should I be doing it in a different manner altogether, I wonder? And if so, what would be considered 'best practice'?
Thank you :)
You can add the ordering in your model:
class Post(models.Model):
...
class Meta:
ordering = ['category', '-date_posted']
See the documentation for more details:
update
Maybe its better to use custom manager for this:
from django.db import models
class CustomManager(models.Manager):
# subclass model manager
def custom_category_dict(self, **kwargs):
# add a new method for building a dictionary
nDict = dict()
for i in self.get_queryset().filter(**kwargs): # filter queryset based on keyword argument passed to this method
current_list = nDict.get(i.category.name, [])
current_list.append(i)
nDict.update({i.category.name: current_list})
return nDict
class Posts(models.Model):
# override posts model with manager
objects = CustomManager()
Usage:
# view
class PostListView(ListView):
...
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
context['category_wise_sorted_posts'] = Posts.objects.custom_category_dict() # you can pass filter logic as well, like Posts.objects.custom_category_dict(author_id=1)
return context
# template
{% for category, posts in category_wise_sorted_posts.items %}
<!-- Or {% for category, posts in user.posts_set.custom_category_dict.items %} -->
{{ category }}
{% for p in posts %}
{{ p.title }}
{% endfor %}
{% endfor %}

Confused by Django 404s for simple pages

Im doing my first Django site myself after going through several tutorials and running into some errors that I can't figure out what the problem is as they are simple page requests.
I was trying to make a category detail view e.g. 127.0.0.1:8000/news and Ive followed the same setup as other pages such as the index and post detail which have worked fine but this is giving a 404.
here are my files
models.py
from django.db import models
from django.core.urlresolvers import reverse
class EntryQuerySet(models.QuerySet):
def published(self):
return self.filter(publish=True)
class Blog(models.Model):
title = models.CharField(max_length = 200)
slug = models.SlugField(max_length = 100, db_index = True)
body = models.TextField()
publish = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey('blog.category')
objects = EntryQuerySet.as_manager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', kwargs={'slug':self.slug})
class Meta:
verbose_name = 'Blog entry'
verbose_name_plural = 'Blog entries'
ordering = ['-created']
class Category(models.Model):
cat_title = models.CharField(max_length = 200, db_index = True)
cat_slug = models.SlugField(max_length = 100, db_index = True)
def __str__(self):
return self.cat_title
def get_absolute_url(self):
return reverse('category_detail', kwargs={'cat_slug':self.cat_slug})
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
views.py
from django.views import generic
from . import models
class index_view(generic.ListView):
queryset = models.Blog.objects.published()
template_name = 'index.html'
class post_view(generic.DetailView):
model = models.Blog
template_name = 'post_view.html'
class category_view(generic.ListView):
model = models.Category
template_name = 'category_view.html'
class category_detail_view(generic.DetailView):
model = models.Category
template_name = 'category_detail_view.html'
class About_page(generic.DetailView):
template_name = 'about.html'
app urls.py
from django.conf.urls import url
from django.contrib import admin
from . import views
urlpatterns = [
url(r'^$', views.index_view.as_view(), name='index'),
url(r'^categories/$', views.category_view.as_view(), name='category_detail'),
url(r'^(?P<slug>\S+)$', views.post_view.as_view(), name='post_detail'),
url(r'^(?P<cat_slug>\S+)$', views.category_detail_view.as_view(), name='category_detail_view'),
url(r'^about/$', views.About_page.as_view(), name='about'),
this is the category detail page 'category_detail_view.html'
{% extends 'base.html' %}
{% block title %} The category detail view page {% endblock %}
{% block category_detail %}
{% for cat_title in object_list %}
<ul>
<li>{{ category.cat_title }}</li>
{% endfor %}
</ul>
{% endblock %}
and the about page
{% extends 'base.html' %}
<h2>This is the about page</h2>
both of these pages return this error message
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/about/
Raised by: blog.views.post_view
No Blog entry found matching the query
I dont understand why 'blog.post_view' is being raised when neither of these templates refer to the post_view.
I have an index page with all published posts listed, a categories page with all categories listed and a post detail page all of which work fine and are almost exactly the same as these views and templates.
When Django resolves the url /about/, it goes through your url patterns in order. It matches the post_detail url pattern, so runs the post_view, treating about as a slug. Since you don't have any posts with the slug about, you get the 404 error.
One solution is to move the about url pattern above the post_detail pattern. You should also change the category url pattern, otherwise it won't work. For example, you could do:
url(r'^about/$', views.About_page.as_view(), name='about'),
url(r'^(?P<slug>\S+)$', views.post_view.as_view(), name='post_detail'),
url(r'^categories/(?P<cat_slug>\S+)$', views.category_detail_view.as_view(), name='category_detail_view'),

Categories