How to add categories at my django blog - django

I developed a django blog application using djangogirls.com tutorial. I am trying to add a blog category but I just can't do it!
I was searching google and stackoverflow.com like crazy , but , being a newbie in python/django I couldn't successfully add categories to my blog.
My models:
from django.db import models
from django.utils import timezone
class Post(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
My views:
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
My urls:
from django.conf.urls import include, url
from . import views
urlpatterns = [
url(r'^$', views.post_list, name='post_list'),
url(r'^post/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
]
My post_list.html:
{% extends 'blog/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|truncatewords:100}}</p>
</div>
{% endfor %}
{% endblock %}
My post_detail.html:
{% 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 %}
Ok. If somebody can help me out , I need to create a category model for this blog model ,I would really appreciate it !
Thanks in advance!

I'd go with
class Category(models.Model):
title = models.CharField(max_length=255, verbose_name="Title")
...
class Post(models.Model):
category = models.ForeignKey(Category, verbose_name="Category")
title = models.CharField(max_length=255, verbose_name="Title")
text = models.TextField()
...
When you have a post like this:
post = Post.objects.first()
you can access it's category's title with post.category.title or when you have a category like this:
category = Category.objects.first()
you can get the posts under that category with category.post_set.all().
I have edited your code to show how I'd write if that was a project I am working on. Here it is:
models.py
from django.db import models
from django.utils import timezone
class Category(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created at")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated at")
title = models.CharField(max_length=255, verbose_name="Title")
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
ordering = ['title']
def __str__(self):
return self.title
class Post(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created at")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated at")
is_published = models.BooleanField(default=False, verbose_name="Is published?")
published_at = models.DateTimeField(null=True, blank=True, editable=False, verbose_name="Published at")
category = models.ForeignKey(Category, verbose_name="Category")
author = models.ForeignKey('auth.User', verbose_name="Author")
title = models.CharField(max_length=200, verbose_name="Title")
text = models.TextField(verbose_name="Text")
class Meta:
verbose_name = "Post"
verbose_name_plural = "Posts"
ordering = ['-created_at']
def publish(self):
self.is_published = True
self.published_at = timezone.now()
self.save()
def __str__(self):
return self.title
views.py
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Category, Post
def category_list(request):
categories = Category.objects.all() # this will get all categories, you can do some filtering if you need (e.g. excluding categories without posts in it)
return render (request, 'blog/category_list.html', {'categories': categories}) # blog/category_list.html should be the template that categories are listed.
def category_detail(request, pk):
category = get_object_or_404(Category, pk=pk)
return render(request, 'blog/category_detail.html', {'category': category}) # in this template, you will have access to category and posts under that category by (category.post_set).
def post_list(request):
posts = Post.objects.filter(published_at__lte=timezone.now()).order_by('published_at')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
urls.py
from django.conf.urls import include, url
from . import views
urlpatterns = [
url(r'^category$', views.category_list, name='category_list'),
url(r'^category/(?P<pk>\d+)/$', views.category_detail, name='category_detail'),
url(r'^$', views.post_list, name='post_list'),
url(r'^post/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
]
I didn't write templates, they are up to you.

Actually this method (publish) is needed just for Django Girls tutorial, to play in shell https://tutorial.djangogirls.org/en/django_orm/
def publish(self):
self.is_published = True
self.published_at = timezone.now()
self.save()
As actions are managed by django admin, i would change
published_at = models.DateTimeField(null=True, blank=True, editable=False, verbose_name="Published at")
To
published_at = models.DateTimeField(null=True, blank=True, editable=True, default=timezone.now, verbose_name="Published at")
So when adding new post, it would set default time and you would be able to change it to whatever suits you.

Related

Django filter results on foreign key

I am attempting to display a list of notes that are attached to a project. I can display the individual project the notes are linked to but I am not able to figure out how to display only the notes related to the project.
My models are:
class Project(models.Model):
title = models.CharField(max_length= 200)
description = models.TextField()
def __str__(self):
return self.title
class ProjectNotes(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
date = models.DateField(auto_now_add=True)
project = models.ForeignKey(Project, default=0, blank=True, on_delete=models.CASCADE, related_name='notes')
def __str__(self):
return self.title
The views:
from django.shortcuts import get_object_or_404, render
from django.urls.base import reverse
from django.views.generic import ListView, DetailView
from django.views.generic.edit import CreateView
from .models import Project, ProjectNotes
class CompanyProjects(ListView):
model = Project
template_name = 'company_accounts/projects.html'
class CompanyProjectsDetailView(DetailView):
model = Project
id = Project.objects.only('id')
template_name = 'company_accounts/project_detail.html'
context_object_name = 'project'
class ProjectNotes(ListView):
model = ProjectNotes
template_name = 'company_accounts/project_notes.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['project'] = get_object_or_404(Project, id=self.kwargs.get('pk'))
return context
class ProjectNotesDetailview(DetailView):
model = ProjectNotes
template_name = 'company_accounts/project_note_detail.html'
The template displays the correct project:
{% extends 'base.html' %}
{% block content %}
<h1>Notes</h1>
{{ project }}
{% for note in notes %}
<div class ="projectnotes-entry">
<h2>{{ note.title }}</h2>
<p>{{ note.body }}</p>
</div>
{% endfor %}
{% endblock content %}
So far I have not been able figure out a way to only display the notes related to the specific project.

Django Page not found (404) when filter services by categories using urls

I'm learning how to use urls in Django and I have a problem. I'm trying to get all services that belongs to a category by clicking on the category link, but when I do that, the browser returns me this error:
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/services/None
Raised by: services_app.views.service_list
No Category matches the given query.
And the url looks:
http://localhost:8000/services/None
I already have a populated data base, and it can display their content just using a djanfo for, but I need it to be displayed by categories. Can anyone help me?
Here are my files:
home_app/models.py
from django.db import models
from django.urls import reverse
class Category(models.Model):
name=models.CharField(primary_key=True, max_length=50)
slug=models.SlugField(unique=True, blank=True, null=True)
image=models.ImageField(upload_to='category_home')
description=models.CharField(max_length=100)
content=models.TextField(max_length=500, default="Service")
created=models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('services_by_category', args=[self.slug])
services_app/models.py
from django.db import models
from home_app.models import Category
class Services(models.Model):
category=models.ForeignKey(Category, on_delete=models.CASCADE)
title=models.CharField(max_length=50)
completed=models.DateField(auto_now_add=False, null=True, blank=True)
content=models.CharField(max_length=50, null=True, blank=True)
image=models.ImageField(upload_to='services_services')
created=models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'Service'
verbose_name_plural = 'Services'
def __str__(self):
return '%s de %s' % (self.category, self.title)
services_app/views.py
from django.shortcuts import render, get_object_or_404
from .models import Services
from home_app.models import Category
def service_list(request,category_slug=None):
category = None
categories = Category.objects.all()
services = Services.objects.all()
if category_slug:
category = get_object_or_404(Category,slug=category_slug)
services = services.filter(category=category)
return render(request, 'services_app/services.html', {'categories':categories, 'category':category, 'services':services,})
services_app/urls.py
from django.urls import path
from services_app import views
urlpatterns = [
path('', views.service_list, name='Services'),
path('<slug:category_slug>', views.service_list, name='services_by_category'),
]
services_app/templates/services_app/services.html
{% extends "home_app/base.html" %}
{% load static %}
{% block content %}
<div id="sidebar">
<br>
<br>
<h3>Categories</h3>
<ul>
{% for c in categories %}
<li>
<h4>{{ c.name }}</h4>
</li>
{% endfor %}
</ul>
</div><br>
<div>
<h1>{% if category %}{{ category.name }}{% endif %}</h1>
{% for service in services %}
<img src="{{service.image.url}}">
{% endfor %}
</div>
{% endblock %}
Also my main urls:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('home_app.urls')),
path('services/', include('services_app.urls')),
path('contact/', include('contact_app.urls')),
]
The problem is you don' t generate the slug of Category:
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
class Category(models.Model):
name=models.CharField(primary_key=True, max_length=50)
slug=models.SlugField(unique=True, blank=True, null=True)
image=models.ImageField(upload_to='category_home')
description=models.CharField(max_length=100)
content=models.TextField(max_length=500, default="Service")
created=models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
def save(self, *args, **kwargs):
if self._state.adding:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('services_by_category', args=[self.slug])
Note that you need to set manually the slug of the
existing Category instances (with the admin, a shell or a migration), or you can simply recreate them.

Getting Image in django teplate from ManyToMany field

It is returning an error : Not Found: /post-detail/6/images/wallpaper002.jpg. I have tried to show image through {{ post_detail.img.all.first.url }} but I could show image in the template, it returns None value.
news.html
'''
<div class="text-center">
<img class="detail_picture img-thumbnail" src="{{ post_detail.img.all.first }}">
</div>
'''
models.py
'''
class Pictures(TimestampedModel):
img = models.ImageField(upload_to='images')
def __str__(self):
return str(self.img)
class Post(TimestampedModel):
title = models.CharField(max_length=128)
lang = models.IntegerField(choices=LANGUAGES, default=1)
short_description = models.CharField(max_length=255, blank=True, null=True)
description = models.TextField(blank=True, null=True)
category = models.ManyToManyField(PostCategoies, blank=True)
img = models.ManyToManyField(Pictures, blank=True)
icon = models.CharField(max_length=128, blank=True, null=True, help_text="Example: fa fa-info")
is_active = models.BooleanField(default=True)
def __str__(self):
return self.title
'''
views.py
'''
from django.shortcuts import render, redirect
from django.contrib import messages
from django.views import View
from .models import Post, Pictures
from django.views.generic import DetailView
from . import models
from django.shortcuts import get_object_or_404
class HomePageView(View):
def get(self, request):
posts = Post.objects.all()[:4]
context = {'posts': posts}
return render(request, 'index.html', context)
class PostDetailView(DetailView):
context_object_name = "post_detail"
model = models.Post
template_name = 'news.html'
'''
urls.py
'''
app_name = 'posts'
urlpatterns = [
path('', HomePageView.as_view(), name=""),
path('post-detail/<int:pk>/', PostDetailView.as_view(), name="post_detail")
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
'''
You are accessing the instance of a Pictures class with your current code. You need to then access the img property:
<div class="text-center">
<img class="detail_picture img-thumbnail" src="{{ post_detail.img.all.first.img.url }}">
</div>
I would also suggest that you protect against the case of not having an instance in the ManyToMany field
{% if post_detail.img.all.first %}
<div class="text-center">
<img class="detail_picture img-thumbnail" src="{{ post_detail.img.all.first.img.url }}">
</div>
{% endif %}
I'd also suggest looking into prefetch_related and/or Subquery as a way to more efficiently fetch these related details. It will help you prevent N+1 queries. You can verify you're not running too many queries with the django debug toolbar.

Django Class Based Views not rendering variables in Template

I'm pretty new to Django and already have read a lot about class-based views before coming here. I'm trying to build a one page website, with dynamic blocks that can be written from the Django admin. My problem is that I cannot manage to render variables from my database in my template. Here's what I wrote:
models.py
from django.db import models
from tinymce.models import HTMLField
class MyResume(models.Model):
subline = models.CharField(max_length=200)
content = HTMLField()
class Meta:
verbose_name = "My Resume"
verbose_name_plural = "My Resume"
def __str__(self):
return "My Resume"
class AboutMe(models.Model):
subline = models.CharField(max_length=200)
content = HTMLField()
cover_img = models.ImageField(upload_to="about_me")
class Meta:
verbose_name = "About me"
verbose_name_plural = "About me"
def __str__(self):
return "About me"
class Experience(models.Model):
subline = models.CharField(max_length=200)
pres_content = models.TextField()
exp_content = HTMLField()
date_exp = models.IntegerField()
class Meta:
verbose_name = "Experience"
verbose_name_plural = "Experiences"
def __str__(self):
return "Experience"
class ProjectPost(models.Model):
title = models.CharField(max_length=200)
technology = models.CharField(max_length=100)
subline = models.CharField(max_length=200)
content = HTMLField()
project_post_cover = models.ImageField(upload_to="projectpost_cover")
class Meta:
verbose_name = "Project Post"
verbose_name_plural = "Project Posts"
def __str__(self):
return self.title
class BlogPost(models.Model):
title = models.CharField(max_length=200)
overview = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
content = HTMLField()
blogpost_thumbnail = models.ImageField(upload_to="blogpost_thumbnail")
class Meta:
verbose_name = "Blog Post"
verbose_name_plural = "Blog Posts"
def __str__(self):
return self.title
class Contact(models.Model):
subline = models.CharField(max_length=200)
presentation_content = HTMLField()
def __str__(self):
return "Contact section text"
views.py
from django.shortcuts import render
from django.views.generic.base import TemplateView
from .models import *
class IndexTemplateView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) # first, call super get context data
context['myresume'] = MyResume.objects.all()
context['aboutme'] = AboutMe.objects.all()
context['experience'] = Experience.objects.all()
context['projectpost'] = ProjectPost.objects.all()
context['blogpost'] = BlogPost.objects.all()
context['contact'] = Contact.objects.all()
return context
core.urls.py:
from django.urls import path
from .views import IndexTemplateView
urlpatterns = [
path('', IndexTemplateView.as_view()),
]
appname.urls.py:
from django.contrib import admin
from django.urls import include, path
from django.conf.urls import url
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^tinymce/', include('tinymce.urls')),
path('', include('core.urls')),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
and a snippet from my index.html template:
{% extends 'base.html' %}
{% load static %}
{% block content %}
(...)
<!-- ==================== ABOUT ==================== -->
<section id="about" class="section">
<h2 class="title">ABOUT</h2>
<div class="section-des">
{{ aboutme.subline }}
</div>
<div class="content-670">
<p>
{{ aboutme.content }}
</p>
</div>
<img class="about-img block-right" data-jarallax-element="0 -40" src="{{ aboutme.cover_img.url }}" alt="">
(...)
{% endblock content %}
All of the variables you've added to your template context are querysets, not objects. But you're trying to read them as individual objects in your template, which is why it doesn't work. You have two options:
Iterate over each queryset in your template:
{% for item in aboutme %}
<h2 class="title">ABOUT</h2>
{{ item.subline }}
{{ item.content }}
{% endfor %}
If you only want to display one of each model, then you need to fix your context method to only return one object, e.g.,
context['aboutme'] = AboutMe.objects.first()
Where here you're just returning the first AboutMe object in the database. If you do this, then your existing template logic will work. How you determine which object to return depends on what your data looks like.
{% for obj in myresume %}
<p>{{ obj.subline }}</p>
{% endfor %}
{% for obj in aboutme %}
<p>{{ obj.subline }}</p>
{% endfor %}
you can call your objects in this way in your templates. Also modify str function in models to get the dynamic content.
class MyResume(models.Model):
subline = models.CharField(max_length=200)
content = HTMLField()
class Meta:
verbose_name = "My Resume"
verbose_name_plural = "My Resume"
def __str__(self):
return self.sublime

My link is no responsive after or not generated after using get_url_absolute

The two links are not working despite of the using get_absolute_ur to get the object url in my models.
How do i configure the code to make my links responsive
PICTUREOF THE SITE IMAGE AND LINKS THAT ARE NOT WORKING
MY VIEWS:BELOW
from django.shortcuts import render
from django.shortcuts import get_object_or_404, render_to_response
from .models import Category, Product
# from django.template import RequestContext
# Create your views here.
def index(request):
page_title = 'Musical Instruments and Sheet Music for Musicians'
return render(request, 'catalog/index.html')
def show_category(request, category_slug=None):
c = get_object_or_404(Category, slug=category_slug)
products = c.product_set.all()
page_title = c.name
meta_keywords = c.meta_keywords
meta_description = c.meta_description
return render(request, 'catalog/category.html', {'c': c})
def show_product(request, product_slug=None):
p = get_object_or_404(Product, slug=product_slug)
categories = p.categories.filter(is_active=True)
page_title = p.name
meta_keywords = p.meta_keywords
meta_description = p.meta_description
return render(request, 'catalog/product.html', {'p': p})
MY MODELS:BELOW
from django.db import models
from django.urls import reverse
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True, help_text='Unique value for product page URL,created from name.')
description = models.TextField()
is_active = models.BooleanField(default=True)
meta_keywords = models.CharField("Meta Keywords", max_length=255,
help_text='Comma-delimited set of SEO keywords for meta tag')
meta_description = models.CharField("Meta Description", max_length=255,
help_text='Content for description meta tag')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'categories'
ordering = ['name']
verbose_name = 'Category'
verbose_name_plural = 'Categories'
def __unicode__(self):
return self.name
def get_absolute_url(self):
reverse('catalog:catalog_category', kwargs={'category_slug': self.slug})
# return 'catalog_category', (), {'category_slug': self.slug}
class Product(models.Model):
name = models.CharField(max_length=50, unique=True)
slug = models.CharField(max_length=50, unique=True, help_text='Unique value for product page URL,created from name.')
brand = models.CharField(max_length=50)
sku = models.CharField(max_length=50)
price = models.DecimalField(max_digits=9, decimal_places=2)
old_price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, default=0.00)
image = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
is_bestseller = models.BooleanField(default=False)
is_featured = models.BooleanField(default=False)
quantity = models.IntegerField()
description = models.TextField()
meta_keywords = models.CharField("Meta Keywords", max_length=255,
help_text='Comma-delimited set of SEO keywords for meta tag')
meta_description = models.CharField("Meta Description", max_length=255,
help_text='Content for description meta tag')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField(Category)
class Meta:
db_table = 'products'
ordering = ['-created_at']
def __unicode__(self):
return self.name
def get_absolute_url(self):
reverse('catalog:catalog_product', kwargs={"product_slug": self.slug})
# return 'product_category', (), {'product_slug': self.slug}
def sale_price(self):
if self.old_price > self.price:
return self.price
else:
return None
MY URLS:
from django.urls import path
from . import views
app_name = 'catalog'
urlpatterns = [
path('', views.index, name='catalog_home'),
path('category/<category_slug>/', views.show_category, name='catalog_category'),
path('product/<product_slug>', views.show_product, name='catalog_product'),
]
MY TEMPLATE:
{% extends "catalog.html" %}
{% block content %}
<h1>{{ c.name }}</h1>
{{ c.description }}
<br /><br />
{% for p in products %}
<div class="product_thumbnail">
<a href="{{ p.get_absolute_url }}">
<img src="{{ MEDIA_URL }}images/thumbnails/{{ p.image}}" alt="{{ p.name }}"
class="bn" />
<br />
{{ p.name }}
</a>
</div>
{% endfor %}
{% endblock %}