Slug not rendering detail page in Django 2.0 - django

What is the best way to use slug in this case and make it work properly. I can see the URL on the browser display the items requested but i am unable to render the detailed page. I cant find where the issue is coming from. When i access 'page_detail' the url is 'http://127.0.0.1:8000/posts/2019/03/23/greetings/', which is correct based on my input but django throw an error to render the page. Error is: TypeError: post_detail() got an unexpected keyword argument 'slug'
MODEL:
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250,
unique_for_date='publish')
author = models.ForeignKey(User, on_delete = models.CASCADE, related_name='blog_posts')
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10,
choices=STATUS_CHOICES,
default='draft')
published = PublishedManager() # Custom Model Manager
def get_absolute_url(self):
''' Canonical URL for post detail.'''
return reverse('snippets:post-detail',
args=[self.publish.year,
self.publish.strftime('%m'),
self.publish.strftime('%d'),
self.slug])
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
URL
app_name = 'snippets'
urlpatterns = [
path('posts/', views.post_list, name='post-list'),
path('posts/<int:year>/<int:month>/<int:day>/<slug:slug>/', views.post_detail, name='post-detail'),
]
VIEWS
def post_list(request):
posts = Post.published.all()
context = {'posts': posts}
return render(request, 'snippets/list.html', context)
def post_detail(request, year, month, day, post):
post = get_object_or_404(Post, slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
return render(request, 'snippets/detail.html', {'post':post})
post_list HTML
{% extends "base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>Blog</h1>
{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% endblock %}
post_detail HTML
{% extends "base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|linebreaks }}
{% endblock %}
I am still stuck. Any help would be much appreciated.

try to change your views.py to
def post_detail(request, year, month, day, slug):
post = get_object_or_404(Post, slug=slug,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
return render(request, 'snippets/detail.html', {'post':post})

Related

I am doing a training project and have encountered a problem

I am doing a training project and have encountered a problem. I try to display the "events" block and the "news" block on the main page, but when I run the loop cycle, only one block is displayed, and the second is not displayed. Explain who knows what I'm doing wrong. I have been solving this problem for three days now. Thanks in advance who will respond to help
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if post %}
{% for post in post %}
{{ post.title }}
{% endfor %}
{% else %}
<p>У вас нет материала</p>
{% endif %}
{% if event %}
{% for event in event %}
{{ event.name }}
{% endfor %}
{% else %}
<p>У вас нет материала</p>
{% endif %}
</body>
</html>
Views:
from django.shortcuts import get_object_or_404, render
from django.views.generic.base import View
from .models import Blog, Event
# Create your views here.
class EventView(View):
def get(self, request):
event = Event.objects.all()
return render(request, "home/home_list.html", {"event": event})
class BlogView(View):
def get(self, request):
post = Blog.objects.all()
return render(request, "home/home_list.html", {"post": post})
Urls:
from django.urls import path
from . import views
urlpatterns = [
path("", views.EventView.as_view()),
path("", views.BlogView.as_view())
]
Models:
from django.db import models
from datetime import date
# Create your models here.
class Blog(models.Model):
"""Новости"""
title = models.CharField("Заголовок", max_length=100)
description = models.TextField("Описание")
descriptionTwo = models.TextField("Описание (второй абзац)", blank=True, default="")
blockquote = models.TextField("Цитата", blank=True, default="")
short = models.TextField("Краткое описание", max_length=300)
poster = models.ImageField("Изображение", upload_to="post/")
prewiew = models.ImageField("Превью", upload_to="post/")
dataPost = models.DateField("Дата публикации", default=date.today)
url = models.SlugField(max_length=160, unique=True, blank=True)
draft = models.BooleanField("Черновик", default=False)
def __str__(self):
return self.title
class Meta:
verbose_name = "Новости"
verbose_name_plural = "Новости"
class Event(models.Model):
"""События"""
name = models.CharField("Заголовок", max_length=100)
adress = models.TextField("Адрес")
description = models.TextField("Описание")
short = models.TextField("Краткое описание")
phone = models.TextField("Контактный номер телефона")
email = models.EmailField()
image = models.ImageField("Изображение", upload_to="event/")
dataStart = models.DateField("Дата старта", default=date.today)
def __str__(self):
return self.name
class Meta:
verbose_name = "Событие"
verbose_name_plural = "События"
Admin:
from django.contrib import admin
from .models import Blog, Event
# Register your models here.
admin.site.register(Event)
admin.site.register(Blog)
Views
You need one view with a context containing both the events ant the posts
class EventView(View):
def get(self, request):
events = Event.objects.all()
posts = Blog.objects.all()
return render(request, "home/home_list.html", {"events": events, "posts":posts})
Template
In the template loop over both
{% if posts %}
{% for p in posts %}
{{ p.title }}
{% endfor %}
{% else %}
<p>У вас нет материала</p>
{% endif %}
{% if events %}
{% for e in events %}
{{ e.name }}
{% endfor %}
{% else %}
<p>У вас нет материала</p>
{% endif %}
In for each loop, use different name for the "loop Object" and the list/dictionary on which you are looping. LIKE THIS:
{% if post %}
{% for p in post %}
{{ p.title }}
{% endfor %}
{% else %}
<p>У вас нет материала</p>
{% endif %}
{% if event %}
{% for e in event %}
{{ e.name }}
{% endfor %}
{% else %}
<p>У вас нет материала</p>
{% endif %}
Did it solve your problem?

Having issues with getting my back-end data to the front-end (Django)

I am creating a blog in which I need a comment section(first project ever, 3 weeks into Python/Django). So far I've created 2 models(Blog which is the main and Comment which is linked with a foreign key to the Blog) but for some reason, I can't find the proper way to display the information from the Comment model into my HTML section.
I've tried with dictionaries, rewrote the models and the views multiple times(due to different youtube videos that I found) but nothing seems to work properly.
These are my models :
class Blog(models.Model):
title = models.CharField('Blog\'s title', max_length=100, blank=False)
slug = models.SlugField('Enter your blog\'s url', blank=False)
date = models.DateTimeField('Date and time on publication',
blank=False)
content = models.TextField(blank=False)
thumb = models.ImageField(blank=True, null=True,
default='default_blog_icon.jpg')
def __str__(self):
return self.title
def snippet(self):
return self.content[:50] + ' ' +'...'
class Comment(models.Model):
post = models.ForeignKey(Blog, on_delete=models.CASCADE,
related_name='comments')
user = models.CharField(max_length=200)
body = models.TextField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
def approved(self):
self.approved = True
self.save()
def __str__(self):
return self.user
The views :
def index(request):
blogs = Blog.objects.all().order_by('-date')
comments = Comment.objects.all()
args = {'blogs': blogs, 'comments': comments}
return render(request, "blog/index.html", args)
def blog_details(request, slug):
slug_url = Blog.objects.get(slug=slug)
return render(request, 'blog/blog_details.html', {'blog_info': slug_url})
And the HTML :
{% for blog in blogs %}
<div class="bloggy">
<h3><a href="{% url 'blog_details' slug=blog.slug %}">{{
blog.title }}</a></h3>
<img src ="/media/{{ blog.thumb }}">
<p>{{ blog.snippet }}</p>
<p>Posted on {{ blog.date }}</p>
<p>{{ comments }}</p>
<p>{{ Comments.Blog.get_user }}</p>
<p>{{ comment.user }}</p>
<p>{{ comment.created }}</p>
<p>{{ comment.approved }}</p>
</div>
{% endfor %}
So far the only thing that I am getting on the front end is the {{ comments }} which is giving me the username that I set. Apart from that nothing else is showing.
I need somehow to display the username, the body of the comment(the content itself) and the date.
Any help, feedback or guidance will be greatly appreciated.
Cheers
You can access the comment for each blog this way:
In your views.py:
def index(request):
blogs = Blog.objects.all().prefetch_related('comments').order_by('-date')
args = {'blogs': blogs}
return render(request, "blog/index.html", args)
Then in your template, you can change the HTML to this:
{% for blog in blogs %}
<div class="bloggy">
<h3>{{blog.title}}</h3>
<img src ="/media/{{ blog.thumb }}">
<p>{{ blog.snippet }}</p>
<p>Posted on {{ blog.date }}</p>
{% for comment in blog.comments.all %}
<p>{{ comment.blog.get_user }}</p>
<p>{{ comment.user }}</p>
<p>{{ comment.created }}</p>
<p>{{ comment.approved }}</p>
{% endfor %}
</div>
{% endfor %}

how to upload the image file and display it

I want to know how to upload and display the image.
I do have the classes in views.py.
class ArticleUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Article
fields = ('title', 'body', 'image', 'source_url')
template_name = 'article_edit.html'
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
template_name = 'article_new.html'
fields = ('title', 'body', 'image', 'source_url')
login_url = 'login'
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
And the relevant classes in the models.py are like as follow.
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(
upload_to='media/', null=True, blank=True)
source_url = models.URLField(blank=True, null=True, max_length=300)
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article_detail', args=[str(self.id)])
class Comment(models.Model):
article = models.ForeignKey(Article,
on_delete=models.CASCADE, related_name='comments', )
comment = models.CharField(max_length=140)
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE),
def __str__(self):
return self.comment
def get_absolute_url(self):
return reverse('article_list')
The article_list.html file is:
{% extends 'base.html' %}
{% load static %}
{% block title %}Articles{% endblock title %}
{% block content %}
{% for article in object_list %}
<div class="card">
<div class="card-header">
<span class="font-weight-bold">{{ article.title }}</span> ·
<span class="text-muted">by {{ article.author }} |
{{ article.date }}</span>
</div>
<div class="card-body">
{{ article.body|linebreaks}}
{% comment %} {% if article.image.url|length > 0 %}
<img src="{{ article.image.url }}" width="200px">
{% else %}
<img src="{% static '/media/mrDoctor.jpg' %}" width="200px" />
{% endif %} {% endcomment %}
<img src="{% static 'articles/mrDoctor.jpg' %}" alt="Image" width="200px" />
Link
Edit
Delete
</div>
<div class="card-footer">
{% for comment in article.comments.all %}
<p>
<span class="font-weight-bold">
{{ comment.author }} ·
</span>
{{ comment }}
</p>
{% endfor %}
</div>
</div>
<br />
{% endfor %}
{% endblock content %}
The user can select the image file from the form.
I can not display the image selected from the input form shown above on the screen shot. I want to display the images dynamically, i.e., when the user choose the image file from the input form. I know I should change the part:{% static '/media/mrDoctor.jpg' %}. When I tried the commented part of article_list.html, i.e., {% if article.image.url|length > 0 %}, it did not work. I will appreciate it if you help me to fix the problem. Many thanks.
After reflecting #Hybrid suggestions, I was able to show the image on the first article but the second and the third one show only the file names.
You can do this by using JavaScript to detect when a user selects an image, and then replacing an <img /> tags src dynamically.
Example code:
<img id="image" />
<input id="files" type="file" />
<script>
document.getElementById("files").onchange = function () {
var reader = new FileReader();
reader.onload = function (e) {
// get loaded data and render thumbnail.
document.getElementById("image").src = e.target.result;
};
// read the image file as a data URL.
reader.readAsDataURL(this.files[0]);
};
</script>

Django - Comment form

I have 4 models: User, Blogger, Post and Comment.
Now, in 'post_desc.html', i want to insert a comment box.
{% if user.is_authenticated %}
<form method="post">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;"></br></br>
<button type="submit">Submit Comment</button>
</form>
{% else %}
<p>Login to comment</p>
{% endif %}
So, this form will only take comment from the user.
But how to store information like 'commented_by' which will the user that is currently logged in and 'commented_on' which will be the post_topic in which he/she is commenting.
How to store these information automatically? in views.py i tried 'request.user' but that didn't worked. Any solutions?
Comment model:
class Comment(models.Model):
commented_by = models.ForeignKey(User, related_name='comments')
commented_on = models.ForeignKey(Post, related_name='comments')
commented_text = models.CharField(max_length=500)
commented_time = models.DateTimeField(auto_now_add=True)
Post model:
class Post(models.Model):
topic = models.CharField(max_length=200)
description = models.TextField()
created_by = models.ForeignKey(User, related_name='posts')
created_on = models.DateTimeField()
I did this in my view.py
def post_desc(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
comment = request.POST['comment']
comments = Comment.objects.create(
commented_text = comment,
commented_on = request.topic,
commented_by = request.user
)
return redirect('post_desc', pk=post.pk)
return render(request, 'post_desc.html', {'post': post})
But it is giving error, "'WSGIRequest' object has no attribute 'post'".
The only thing wrong with your code is that there's no such thing as request.topic. The topic is the Post, which you already have.
comments = Comment.objects.create(
commented_text = comment,
commented_on = post,
commented_by = request.user
)
you have to a create a url that have a pk parameter to know where to post the comment in which post?:
url(r'add/comment/post/(?P<pk>[0-9]+)', views.new_comment, name="comment")
now let's create the view new_comment:
from django.utils import timezone
def new_comment(request, pk):
user = request.user
comment_text = request.POST['your-comment-input-name']
post = Post.objects.get(id=pk)
Comment.objects.create(created_by=user, text=comment_text, post=post, create_at=timezone.localtime(timezone.now()))
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
don't forget to set your form action to action="{% url 'comment' Post.id %}"
I have something like that in my blog project which I develop for improve myself in django python.
my model;
class Comment(models.Model):
post = models.ForeignKey('post.Post', related_name='comments', on_delete=models.CASCADE)
name = models.CharField(max_length=150, verbose_name='Your Name')
comment = models.TextField(verbose_name='Your Comment')
created_date = models.DateTimeField(auto_now_add=True)
my view for post detail page;
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
form = CommentForm(request.POST or None)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return HttpResponseRedirect('your redirect page')
context = {
'post': post,
'form': form,
}
return render(request, 'post/detail.html', context)
my post detail template page;
<h3>Comments;</h3>
{% if post.comments.count <= 0 %}
<h4>No comment yet!</h4>
{% else %}
{% for comment in post.comments.all %}
<h4>{{ comment.name }} |
<small>{{ comment.created_date|timesince }}</small>
</h4>
<p>{{ comment.comment|linebreaks }}</p>
<hr/>
{% endfor %}
{% endif %}
<h3>Add Comment:</h3>
{% include 'post/comment.html' %}
my comment form template;
{% load crispy_forms_tags %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-primary" type="submit" value="Add Comment"/>
</form>
Hope this helps.

Django Filtering A Blog Based on Tags

I'm trying to filter my blog based on tags. I've managed to work out how to display all the tags, however now I want the user to have the ability to click on a tag and bring up a filter list of blog posts based on that tag.
I'm getting an error:
ValueError invalid literal for int() with base 10: 'testtag'
url.py
urlpatterns = [
# Examples: /blogs
url(r'^tags$', 'blog.views.tags', name='tags'),
url(r'^tags/(?P<blog_tagslug>[\w-]+)$', 'blog.views.tagslist', name='tagslist'),
views.py
def tags(request):
blog_obj = Tag.objects.all()
paginator = Paginator(blog_obj, 5)
page = request.GET.get('page')
try:
blog_list = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
blog_list = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
blog_list = paginator.page(paginator.num_pages)
template_hash = {'blog_list':blog_list}
return render(request,'blog/tags.html', template_hash)
def tagslist(request, blog_tagslug):
#blog = Blog.objects.get(pk=blog_id)
#blog = Tag.objects.all().filter(tagslug=blog_tagslug)
blog = Blog.objects.all().filter(tags=blog_tagslug)
paginator = Paginator(blog, 2)
page = request.GET.get('page')
try:
blog_list = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
blog_list = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
blog_list = paginator.page(paginator.num_pages)
template_hash = {'blog_list':blog_list}
return render(request,'blog/index.html', template_hash)
tags.html (works fine)
<h1>All Tags</h1>
{% if blog_list %}
<ul>
{% for blog in blog_list %}
{{ blog.image.thumbnail.url }}
<li>{{ blog.tagslug }}</li>
{{ blog.tags.all|join:", " }} <br>
{% endfor %}
</ul>
{% else %}
<p>No blogs are available.</p>
{% endif %}
<div class="pagination">
<span class="step-links">
{% if blog_list.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ blog_list.number }} of {{ blog_list.paginator.num_pages }}.
</span>
{% if blog_list.has_next %}
next
{% endif %}
</span>
</div>
tagslist.html (doesn't work gets error)
<h1>Blog Index</h1>
{% if blog_list %}
<ul>
{% for blog in blog_list %}
{{ blog.image.thumbnail.url }}
<li>{{ blog.blog_title }}</li>
{{ blog.tags.all|join:", " }} <br>
{% endfor %}
</ul>
{% else %}
<p>No blogs are available.</p>
{% endif %}
<div class="pagination">
<span class="step-links">
{% if blog_list.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ blog_list.number }} of {{ blog_list.paginator.num_pages }}.
</span>
{% if blog_list.has_next %}
next
{% endif %}
</span>
</div>
models.py
from django.db import models
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.conf import settings
# Create your models here.
class Image(models.Model):
name = models.CharField(max_length = 255)
image = models.ImageField(upload_to="%Y/%m/%d",max_length=500,blank=True,null=True)
thumbnail = models.ImageField(upload_to="%Y/%m/%d",max_length=500,blank=True,null=True)
def __str__(self):
return self.name
class Tag(models.Model):
tagslug = models.SlugField(max_length=200, unique=True)
def __str__(self):
return self.tagslug
class Author(models.Model):
authorname = models.CharField(max_length=200, null=True)
def __str__(self):
return self.authorname
class Blog(models.Model):
blog_title = models.CharField(max_length = 200)
author = models.ForeignKey(Author)
image = models.ForeignKey(Image)
blog_content = models.TextField()
slug = models.SlugField(max_length=200, unique=True)
publish = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
tags = models.ManyToManyField(Tag)
user_id = models.ForeignKey(User)
def __str__(self):
return self.blog_title
class Comment(models.Model):
comment_content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
blog_id = models.ForeignKey(Blog)
user_id = models.ForeignKey(User)
def __str__(self):
return '{0}, {1}'.format(self.comment_content, self.created)
The problem is that tags expect a list of Tag objects whereas you pass a string. You should filter blogs by tag's slug as follows:
blog = Blog.objects.all().filter(tags__tagslug=blog_tagslug)