Django Blog url error NoReverseMatch at / - django

I git this error home.html page when I but the url to user posts page the url is work fine in other pages
but in home page I get this error
Reverse for 'user-posts' with arguments '('',)' not found. 1 pattern(s) tried: ['user/(?P[^/]+)/$']
I don't know if their something I need to but in home.html to let me access the post.author.usernname
home.html
{% extends "blog/base.html" %}
{% block content %}
<h1>Blog Home!</h1>
{% for post in posts %}
<article class="media content-section">
<img class = "rounded-circle article-img" src = "{{ post.author.profile.image.url }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-posts' post.author.username %}">{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted|date:"F d, Y" }}</small>
</div>
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content }}</p>
</div>
</article>
{% endfor %}
{% if is_paginated %}
{% if page_obj.has_previous %}
<a class = "btn btn-outline-info mb-4" href="?page=1">First</a>
<a class = "btn btn-outline-info mb-4" href="?page={{ page_obj.previous_page_number }}">Previous</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<a class = "btn btn-info mb-4" href="?page={{ num }}">{{ num }}</a>
{% elif num > page_obj.number|add:'-4' and num < page_obj.number|add:'4' %}
<a class = "btn btn-outline-info mb-4" href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class = "btn btn-outline-info mb-4" href="?page={{ page_obj.next_page_number }}">Next</a>
<a class = "btn btn-outline-info mb-4" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
{% endif %}
{% endblock %}
url.py
from django.urls import path
from .views import (
PostListView,
PostDetailView,
PostCreateView,
PostUpdateView,
PostDeleteView,
UserPostListView
)
from . import views
urlpatterns = [
path('', PostListView.as_view(), name= "blog-home"),
path('user/<str:username>/', UserPostListView.as_view(), name= "user-posts"),
path('post/<int:pk>/', PostDetailView.as_view(), name= "post-detail"),
path('post/new/', PostCreateView.as_view(), name= "post-create"),
path('post/<int:pk>/update/', PostUpdateView.as_view(), name= "post-update"),
path('post/<int:pk>/delete/', PostDeleteView.as_view(), name= "post-delete"),
path('about/', views.about, name= "blog-about"),
]
the views.py
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.models import User
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
from .models import Post
def home(request):
content = {
'posts':Post.objects.all()
}
return render(request, 'blog/home.html', content)
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
class UserPostListView(ListView):
model = Post
template_name = 'blog/user_posts.html'
context_object_name = 'posts'
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username = self.kwargs.get('username'))
return Post.objects.filter(author = user).order_by('-date_posted')
class PostDetailView(DetailView):
model = Post
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = '/'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
def about(request):
return render(request, 'blog/about.html', {'title': 'About'})
the models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length = 100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete = models.CASCADE, db_constraint=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs = {'pk':self.pk})

Related

get_absolute_url creates blank link

get_absolute_url doesn't create url. Clicking in title of the post must lead to detail of the post. While I manually create url in address bar as blog/2020/1/3/check-text, it works. What could be the possible problem? Is it something to do with python version?
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class PublishedManager(models.Manager):
def get_queryset(self):
return super(PublishedManager, self).get_queryset().filter(status='published')
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)
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')
objects = models.Manager() # the default manager
published = PublishedManager() # our custom manager
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post_detail', args = [
self.publish.year,
self.publish.month,
self.publish.day,
self.slug,
])
views.py
from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator, PageNotAnInteger,\
EmptyPage
from django.views.generic import ListView
from .models import Post
from .forms import EmailPostForm
from django.core.mail import send_mail
class PostListView(ListView):
queryset = Post.published.all()
context_object_name = 'posts'
paginate_by = 3
template_name = 'blog/post/list.html'
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,
'blog/post/detail.html',
{'post': post})
urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.PostListView.as_view(), name='post_list'),
path('<int:year>/<int:month>/<int:day>/<slug:post>/',
views.post_detail,
name='post_detail'),
path('<int:post_id>/share/',
views.post_share, name='post_share'),
]
list.html
{% extends 'blog/base.html' %}
{% block title %}My Blog{% endblock%}
{% block content %}
<h1>My 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 %}
<!-- {% include '../pagination.html' with page=posts %} -->
{% include '../pagination.html' with page=page_obj %}
{% endblock %}
detail.html
{% extends 'blog/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 }}
<p>
<a href="{% url 'blog:post_share' post.id %}">
Share this post
</a>
</p>
{% endblock %}
see href is empty

Can't add comment form in Django web application

I have a trouble adding form-group (I believe it's bootstrap class).
The form-group doesn't do anything at all, or maybe it's a problem with form.author and form-body variables!?
More simply, I need UI comment section (now only I can add and edit comments from django admin page). Some code:
post_details.html
<article class="media content-section">
<form action="/post/{{ post.slug }}/" method="post">
{% csrf_token %}
<div class="form-group">
{{ form.author }}
</div>
<div class="form-group">
{{ form.body }}
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<ul>
{% for comment in post.comments.all %}
<p>
<b>#{{ comment.author }}</b>
<small>{{ comment.created_date }} </small>
</p>
<p> {{ comment.text }}</p>
<hr>
{% if comment.replies.all %}
<ul>
{% for reply in comment.replies.all %}
<p>{{ reply.text }}</p>
<hr>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
<ul>
</article>
forms.py
from django import forms
class CommentForm(forms.Form):
author = forms.CharField(
max_length=60,
widget=forms.TextInput(
attrs={"class": "form-control", "placeholder": "Your Name"}
),
)
body = forms.CharField(
widget=forms.Textarea(
attrs={"class": "form-control", "placeholder": "Leave a comment!"}
)
)
views.py
def comment(request):
form = CommentForm()
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = Comment(
author=form.cleaned_data["author"],
body=form.cleaned_data["body"],
post=post,
)
comment.save()
context = {"post": post, "comments": comments, "form": form}
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = Comment(
author=form.cleaned_data["author"],
body=form.cleaned_data["body"],
post=post
)
comment.save()
comments = Comment.objects.filter(post=post)
context = {
"post": post,
"comments": comments,
"form": form,
}
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.text
EDIT:
urls.py
from django.urls import path
from django.conf.urls import include, url
from . import views
from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView, UserPostListView
urlpatterns = [
#Blog section
path("", PostListView.as_view(), name='blog-home'),
path("user/<str:username>", UserPostListView.as_view(), name='user-posts'),
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
path("posts/new/", PostCreateView.as_view(), name='post-create'),
path("post/<slug:slug>/update/", PostUpdateView.as_view(), name='post-update'),
path("post/<slug:slug>/delete/", PostDeleteView.as_view(), name='post-delete'),
path("about/", views.about, name="blog-about"),
path("<category>/", views.blog_category, name="blog_category"),
]
I really need something like this (tried to follow this tutorial, but nothing works well :
My comment section:
I've looked into that tutorial and implemented myself. Here goes the answer:
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.blog_index, name="blog_index"),
path("<slug:slug>/", views.post_detail, name="post_detail"),
path("<category>/", views.blog_category, name="blog_category"),
]
models.py
from django.db import models
from django.utils.text import slugify
class Category(models.Model):
name = models.CharField(max_length=20)
class Post(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField("Category", related_name="posts")
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
class Comment(models.Model):
author = models.CharField(max_length=60)
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey("Post", on_delete=models.CASCADE)
post_detail.html
{% extends "blog_app/base.html" %}
{% block page_content %}
<div class="col-md-8 offset-md-2">
<h1>{{ post.title }}</h1>
<small>
{{ post.created_on.date }} |
Categories:
{% for category in post.categories.all %}
<a href="{% url 'blog_category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</small>
<p>{{ post.body | linebreaks }}</p>
<h3>Leave a comment:</h3>
<form action="/blog/{{ post.pk }}/" method="post">
{% csrf_token %}
<div class="form-group">
{{ form.author }}
</div>
<div class="form-group">
{{ form.body }}
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<h3>Comments:</h3>
{% for comment in comments %}
<p>
On {{comment.created_on.date }}
<b>{{ comment.author }}</b> wrote:
</p>
<p>{{ comment.body }}</p>
<hr>
{% endfor %}
</div>
{% endblock %}
views.py
def post_detail(request, slug):
post = Post.objects.get(slug=slug)
comments = Comment.objects.filter(post=post)
form = CommentForm()
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = Comment(
author=form.cleaned_data["author"],
body=form.cleaned_data["body"],
post=post,
)
comment.save()
context = {"post": post, "comments": comments, "form": form}
return render(request, "blog_app/post_detail.html", context)
Edit
I've changed the code to support slug field generation from the title. I'm not handling exception, thus you gonna have look into it by yourself. Good luck.
I think the problem is you're using a Form and not a ModelForm.
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['author', 'text']
...
In your file views.py you have duplicated code and you don't have the return
statement:
return render(request, "post_details.html", context)

How to add slug in Django Web-application

Here is the problem: I'm trying to 'fresh' my django web blog, so instead of having /post/2/ I want to have slugged link that's exactly like my title (smth like this: /post/today-is-friday
Here is some code, I've tried couple of things, but there is nothing working:
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from django.template.defaultfilters import slugify
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
categories = models.ManyToManyField('Category', related_name='posts')
image = models.ImageField(upload_to='images/', default="images/None/no-img.jpg")
slug= models.SlugField(max_length=500, unique=True, null=True, blank=True)
def save(self, *args, **kwargs):
self.url= slugify(self.title)
super(Post, self).save(*args, **kwargs)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Category(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
urls.py
from django.urls import path
from django.conf.urls import include, url
from . import views
from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView, UserPostListView
urlpatterns = [
#Blog section
path("", PostListView.as_view(), name='blog-home'),
path("user/<str:username>", UserPostListView.as_view(), name='user-posts'),
path("<slug:slug>/", PostDetailView.as_view(), name='post-detail'),
path("post/new/", PostCreateView.as_view(), name='post-create'),
path("<slug:slug>/update/", PostUpdateView.as_view(), name='post-update'),
path("<slug:slug>/delete/", PostDeleteView.as_view(), name='post-delete'),
path("about/", views.about, name="blog-about"),
path("<category>/", views.blog_category, name="blog_category"),
]
user_posts.html(this is for accessing blog post itself)
{% extends 'blog/base.html' %}
{% block content %}
<h1 class='mb-3'>Post by {{ view.kwargs.username }} ({{ page_obj.paginator.count }})</h1>
{% for post in posts %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ post.author.profile.image.url }}" alt="">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2 author_title" href="{% url 'user-posts' post.author.username %}">#{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted|date:"N d, Y" }}</small>
<div>
<!-- category section -->
<small class="text-muted">
Categories:
{% for category in post.categories.all %}
<a href="{% url 'blog_category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</small>
</div>
</div>
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content|slice:200 }}</p>
</div>
</article>
{% endfor %}
{% endblock content %}
post_form.html(It's for creating a new post, have trouble with redirecting after post created)
{% extends 'blog/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Blog Post</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
If you are going to change the value of the slug field before saving, you can use signals.
Also the slugify method of django is located in django.utils.text not django.template.defaultfilters.
urls.py
# ...
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
# ...
models.py
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.text import slugify
import string
class Post(models.Model):
# ...
slug= models.SlugField(max_length=500, unique=True, null=True, blank=True)
# do not override save method here
def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def unique_slug_generator(instance, new_slug=None):
if new_slug is not None:
slug = new_slug
else:
slug = slugify(instance.title)
class_ = instance.__class__
qs_exists = class_.objects.filter(slug=slug).exists()
if qs_exists:
new_slug = f"{slug}-{random_string_generator(size=5)}"
return unique_slug_generator(instance, new_slug=new_slug)
return slug
#receiver(pre_save, sender=Post)
def post_pre_save_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = unique_slug_generator(instance)
Those two functions, unique_slug_generator and random_string_generator, together will guaranty that you won't have same slug on two posts, even if the title of those posts are same! (it will add some randomly generated string at the end)
Edit
In your html template for user_posts.html, replace
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
with
<h2><a class="article-title" href="{% url 'post-detail' post.slug %}">{{ post.title }}</a></h2>
Also, in your view (not template) of post_form, you should override get_success_url like this:
def get_success_url(self):
return reverse('post-detail', kwargs={'slug': self.object.slug})
Edit 2 (for more clarification)
First, we need a url for each post, we implement it as:
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
Next, you should change your previous links to the post-detail. These include your 1)template links and 2)links in the views/models:
In your templates, wherever you have {% url 'post-detail' post.pk %}, you should change that to {% url 'post-detail' post.slug %}.
and in your views/models, you should change reverse('post-detail', kwargs={'pk': self.pk}) tp reverse('post-detail', kwargs={'slug': self.slug}) (not self.object.slug)

Why does a Contact Form fail to work with jinja tags?

I can successfully create a contact form with Django (2.1).
When I put the form tags inside with {% csrf_token %} inside a {% block content %}{% endblock %} in a HTML template, I cannot get the "success" page to "fire".
Why would this happen when I use the jinja tags?
Middleware:
'django.middleware.csrf.CsrfViewMiddleware',
urls.py
from django.conf.urls import url, include
from django.urls import path
from . import views
# from django.contrib.auth.views import login
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^$', views.about, name='about'),
url(r'^$', views.howitworks, name='services'),
url(r'^$', views.showroom, name='showroom'),
url(r'^privacy/', views.privacy, name='privacy'),
url(r'^terms-of-service/', views.tos, name='terms-of-service'),
url(r'^contact/', views.emailView, name='contact'),
# path('email/', views.emailView, name='email'),
path('success/', views.successView, name='success'),
]
views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail, BadHeaderError
from .forms import ContactForm
def index(request):
return render(request, 'home/landing.html')
def about(request):
return render(request, 'home/landing#about.html')
def howitworks(request):
return render(request, 'home/landing#services.html')
def showroom(request):
return render(request, 'home/landing#portfolio.html')
def privacy(request):
return render(request, 'home/privacy.html')
def tos(request):
return render(request, 'home/terms-of-service.html')
# def contact(request):
# return render(request, 'home/contact.html')
# def login(request):
# return render(request, 'auth/account/login.html')
# def signup(request):
# return render(request, 'auth/account/signup.html')4
def emailView(request):
if request.method == 'GET':
form = ContactForm()
else:
form = ContactForm(request.POST)
if form.is_valid():
first_name = form.cleaned_data['first_name']
last_name = form.cleaned_data['last_name']
subject = form.cleaned_data['subject']
from_email = form.cleaned_data['from_email']
message = form.cleaned_data['message']
try:
send_mail(subject, message, from_email, ['peterdoh#gmail.com'])
except BadHeaderError:
return HttpResponse('Invalid header found.')
return redirect('success')
return render(request, "home/contact.html", {'form': form})
def successView(request):
# return HttpResponse('Success! Thank you for your message to Revs Direct.')
return render(request, "home/success.html", {})
contact.html
{% extends "home/base.html" %}
{% load staticfiles %}
{% block header %}
{% include 'home/header1.html' %}
{% endblock %}
{% block content %}
<h3 class="section-title">Contact Us</h3>
<p class="section-description">We d love to hear from you. Please fill in the form below with any queries or questions that
you may have.</p>
<div class="container wow fadeInUp">
<div class="row justify-content-center">
<div class="col-lg-3 col-md-4">
<div class="info">
<!-- <div>
<i class="fa fa-map-marker"></i>
<p>A108 Adam Street<br>New York, NY 535022</p>
</div> -->
<!-- <div>
<i class="fa fa-envelope"></i>
<p>info#revsdirect.com</p>
</div> -->
<!-- <div>
<i class="fa fa-phone"></i>
<p>HOLD</p>
</div> -->
</div>
<div class="social-links">
<h3>Social Media</h3>
<a href="#" class="facebook">
<i class="fab fa-facebook-f"></i>
</a>
<a href="#" class="instagram">
<i class="fab fa-instagram"></i>
</a>
<a href="#" class="twitter">
<i class="fab fa-twitter"></i>
</a>
<!-- <i class="fa fa-google-plus"></i>
<i class="fa fa-linkedin"></i> -->
</div>
</div>
<div class="col-lg-5 col-md-8">
<div class="form">
<!-- <div id="sendmessage">Your message has been sent. Thank you!</div>
<div id="errormessage"></div> -->
<form role="form" action="" method="post" class="contactForm">{% csrf_token %}
{{ form.as_p }}
<div class="form-actions">
<button type="submit">Send</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
Have you saved your form?
if form.is_avlid():
myform = form.save(commit=False)
# add as per your requirement
myform.save()
return statement
I found a working solution using guidance from https://hellowebbooks.com/news/tutorial-setting-up-a-contact-form-with-django/.
In contact.html, I used:
<div class="container">
<br>
<br>
<h2>Contact</h2>
<p>Send me a message</p>
{% if messages %}
<div class="row">
<div class="col-xs-12">
{% for message in messages %}
<p {% if message.tags %} class="{{ message.tags }}" {% endif %}>{{ message }}</p>
{% endfor %}
</div>
</div>
{% endif %}
<form role="form" action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button class="button primary type=" submit">Send Message</button>
</form>
</div>
Then in my forms.py:
from django import forms
class ContactForm(forms.Form):
contact_name = forms.CharField(required=True)
contact_email = forms.EmailField(required=True)
content = forms.CharField(
required=True,
widget=forms.Textarea(attrs={'rows': 4, 'cols': 40})
)
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
self.fields['contact_name'].label = "Your name:"
self.fields['contact_email'].label = "Your email:"
self.fields['content'].label = "Your message:"
and finally, my views.py:
from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseRedirect
from .forms import ContactForm
from django.contrib import messages
from django.core.mail import EmailMessage
from django.template.loader import get_template
# Create your views here.
def index(request):
form_class = ContactForm
# new logic!
if request.method == 'POST':
form = form_class(data=request.POST)
if form.is_valid():
contact_name = request.POST.get('contact_name', '')
contact_email = request.POST.get('contact_email', '')
form_content = request.POST.get('content', '')
# Email the profile with the
# contact information
template = get_template('landing/contact_template.txt')
context = {
'contact_name': contact_name,
'contact_email': contact_email,
'form_content': form_content,
}
content = template.render(context)
email = EmailMessage(
"[Peter Doherty] New contact form submission",
content,
"Peter Doherty" +'',
['peterdoh#gmail.com'],
headers = {'Reply-To': contact_email }
)
email.send()
messages.success(request, 'Thank you. Message sent.')
# return redirect('^$')
return render(request, 'landing/index.html', {'form': form_class,})
It was unclear why the first approach was throwing up an error. This approach works and have tested in. Also, ensure that your email configuration is correct in the settings.py file.

Pagination Django Class Based View Not Working Properly

I'm trying to use Django (v2.0) pagination with CBV and got issues. The pagination is active because the tag {% if is_paginated %} returns "True" and shows the "PagNav" and the browser path changes too, like this (..?page=1, ...?page=2, etc...) but the displayed elements are all, not 3 just as I set in "paginate_by=3". For example, if the query has 15 elements must to show 3 elements per page and 1 to 5 in the pagination down bellow, but shows all elements. I attach an image and the code:
models.py:
from django.db import models
from django.contrib.auth.models import User
from ckeditor.fields import RichTextField
# Create your models here.
class Project(models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE, default=1)
name = models.CharField(verbose_name='Nombre del proyecto', max_length=200)
client = models.CharField(verbose_name='Nombre del cliente', max_length=200)
description = RichTextField(verbose_name='Descripción')
start = models.DateField(verbose_name='Fecha de Inicio', null=True, blank=True)
ending = models.DateField(verbose_name='Fecha de Finalización', null=True, blank=True)
order = models.SmallIntegerField(verbose_name="Orden", default=0)
created = models.DateTimeField(verbose_name='Fecha de creación', auto_now_add=True)
updated = models.DateTimeField(verbose_name='Fecha de modificación', auto_now=True)
class Meta:
verbose_name = 'Proyecto'
verbose_name_plural = 'Proyectos'
ordering = ['-start', 'order']
def __str__(self):
return self.name
class Album(models.Model):
project = models.ForeignKey(Project, verbose_name='Proyecto relacionado', on_delete = models.CASCADE)
title = models.CharField(verbose_name='Título de la imagen', max_length=200)
image = models.ImageField(verbose_name='Imagen', upload_to='portfolio')
created = models.DateTimeField(verbose_name='Fecha de creación', auto_now_add=True)
updated = models.DateTimeField(verbose_name='Fecha de modificación', auto_now=True)
class Meta:
verbose_name = 'Imagen en el album'
verbose_name_plural = 'Imágenes en el album'
ordering = ['created']
def __str__(self):
return self.title
views.py:
#method_decorator(staff_member_required(login_url='login'), name='dispatch')
class AlbumListView(SingleObjectMixin, ListView):
paginate_by = 3
template_name = "core/album_list_form.html"
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=Project.objects.all())
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['project'] = self.object
return context
def get_queryset(self):
return self.object.album_set.all()
#method_decorator(staff_member_required(login_url='login'), name='dispatch')
class ProjectUpdateView(UpdateView):
model = Project
template_name = "core/project_update_form.html"
form_class = ProjectUpdateForm
def get_success_url(self):
return reverse_lazy('portfolio_update', args=[self.object.id]) + '?ok'
urls.py:
from django.urls import path
from . import views
from .views import *
urlpatterns = [
path('', HomePageView.as_view(), name="home"),
path('album/list/<int:pk>', AlbumListView.as_view(), name="portfolio_list_album"),
# Update Views
path('project/update/<int:pk>', ProjectUpdateView.as_view(), name="portfolio_update"),
.
.
.
album_list_form.html:
{% extends "core/base.1.html" %}
{% load static %}
{% block title %}Imágenes de {{project.name}}{% endblock %}
{% block content %}
<div class="container">
<div class="row mt-5 mb-2 ml-1">
<div class="mt-3">
<h2 class="mt-5 mb-0"><a style="color: #343a40;" href="{% url 'portfolio_update' project.id %}">{{project.name}}</a></h2>
<div class="subheading mb-5">Imágenes:</div>
</div>
</div>
</div>
<div class="album py-5 bg-light">
<div class="container" style="margin-bottom: 2.5rem!important; margin-top: 2.5rem!important;">
<div class="row">
{% for album in project.album_set.all %}
<div class="col-md-4">
<div class="card mb-4 shadow-sm">
<img class="card-img-top border-bottom p-2 p-2 p-2 p-2 bg-light" src="{{album.image.url}}" alt="Card image cap">
<div class="card-body">
<p class="card-text" title="{{album.title}}" style="color: #343a40;"><strong>{{album.title|truncatechars:31}}</strong></p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-warning"><i class="fa fa fa-pencil"></i></button>
<button type="button" class="btn btn-sm btn-outline-danger"><i class="fa fa fa-times"></i></button>
</div>
<p></p>
</div>
<small class="text-muted">Última modificación: {{album.updated|date:"SHORT_DATE_FORMAT"}} {{album.updated|time:"h:i a"}}</small>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% if is_paginated %}
<nav aria-label="Page navigation example">
<ul class="pagination pagination-lg justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" style="color:#bd5d38;" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
</li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item"><a class="page-link" style="color:#ffffff; background-color: #343a40;" href="?page={{ i }}">{{ i }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" style="color:#bd5d38;" href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" style="color:#bd5d38;" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
{% endblock %}
The core of the problem I think is that you write in the template:
{% for album in project.album_set.all %}
You thus make a project object. But regardless whether you paginate your class-based view, you will not paginate a related object manager. You only paginate the object_list.
Probably you can solve it with:
{% for album in object_list %}
Furthermore I think you make the class-based view extremely complicated: this is a class-based view over Albums. Yes, filtered albums, but so the Album should be central here. I think it can be rewritten to:
from django.urls import path
from . import views
from .views import *
urlpatterns = [
path('', HomePageView.as_view(), name="home"),
path('album/list/<int:project_pk>', AlbumListView.as_view(), name="portfolio_list_album"),
path('project/update/<int:pk>', ProjectUpdateView.as_view(), name="portfolio_update"),
]
Then in the view itself:
#method_decorator(staff_member_required(login_url='login'), name='dispatch')
class AlbumListView(ListView):
model = Album
paginate_by = 3
template_name = "core/album_list_form.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['project'] = Project.objects.get(pk=self.kwargs['project_pk'])
return context
def get_queryset(self):
return Album.objects.filter(project_id=self.kwargs['project_pk'])