I am doing a project in django 1.9.9/python 3.5, for exercise reasons I have a blog app, an articles app and a comments app. Comments app has to be genericly related to blog and articles. My problem is that the templates are not showing my comments. Comments are being created and related to their post/article because I can see it in admin, so it is not a comment creation problem. They are simply not showing in my template.
My comments/models.py:
from django.db import models
class Comment(models.Model):
post = models.ForeignKey('blog.Entry',related_name='post_comments', blank=True, null=True)
article = models.ForeignKey('articles.Article', related_name='article_comments', blank=True, null=True)
body = models.TextField()
created_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.body
My commments/views.py:
from django.utils import timezone
from django.shortcuts import render, get_object_or_404
from django.shortcuts import redirect
from .forms import CommentForm
from .models import Comment
from blog.models import Entry
from articles.models import Article
from blog.views import post_detail
from articles.views import article_detail
def article_new_comment(request, pk):
article = get_object_or_404(Article, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.article = article
comment.created_date=timezone.now()
comment.save()
return redirect(article_detail, pk=article.pk)
else:
form=CommentForm()
return render(request, 'comments/add_new_comment.html', {'form': form})
def blog_new_comment(request, pk):
post = get_object_or_404(Entry, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.created_date = timezone.now()
comment.save()
return redirect(post_detail, pk=post.pk)
else:
form=CommentForm()
return render(request, 'comments/add_new_comment.html', {'form': form})
And here is my post_detail.html, where comments should be. I will not post article_detail.html because they are exactly the same:
{% extends 'blog/base.html' %}
{% block content %}
<div class="post">
{% if post.modified_date %}
<div class="date">
{{ post.modified_date }}
</div>
{% else %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"> Edit Post </span></a>
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaksbr }}</p>
<hr>
<a class="btn btn-default" href="{% url 'new_blog_comment' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"> Add Comment </span></a>
{% for comment in post.comments.all %}
<div class="comment">
<div class="date">{{ comment.created_date }}</div>
<p>{{ comment.body|linebreaksbr }}</p>
</div>
{% empty %}
<p>No comments here yet</p>
{% endfor %}
</div>
{% endblock %}
Let me know if any other file would help you to help me, like blog/models, views, although I don't think the problem is there.
You've explicitly set the related name of comments on your post to post_comments. So you would have to access them like:
{% for comment in post.post_comments.all %}
This is assuming post in your template refers to an instance of the Entry model.
Related
Whenever I click on the profiles(links) on the profile_list page, I get the profile doesn't exist error
it always goes to profiles that do not longer exist in the database.
Models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class Profile(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
follows=models.ManyToManyField(
"self",
related_name="followed_by",
symmetrical=False,
blank=True)
def __str__(self):
return self.user.username
#receiver(post_save,sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
user_profile=Profile(user=instance)
user_profile.save()
user_profile.follows.add(instance.profile)
user_profile.save()
admin.py:
from django.contrib import admin
from django.contrib.auth.models import Group,User
from .models import Profile
class ProfileInline(admin.StackedInline):
model=Profile
class UserAdmin(admin.ModelAdmin):
model=User
fields=["username"]
inlines=[ProfileInline]
# Register your models here.
admin.site.unregister(Group)
admin.site.unregister(User)
admin.site.register(User,UserAdmin)
views.py:
from django.urls import reverse
from django.shortcuts import render
from requests import request
from users.models import Profile
from django.http import HttpResponseRedirect
# Create your views here.
def dashboard(request):
return render(request,"inbox/layout.html")
def feed(request):
return render(request,"inbox/feed.html")
def outbox(request):
return render(request,"inbox/outbox.html")
def profile_list(request):
profiles=Profile.objects.exclude(user=request.user)
return render(request,"inbox/profile_list.html",{"profiles":profiles})
def profile(request, pk):
if not hasattr(request.user, 'profile'):
missing_profile = Profile(user=request.user)
missing_profile.save()
profile = Profile.objects.get(pk=pk)
if request.method == "POST":
current_user_profile = request.user.profile
data = request.POST
action = data.get("follow")
if action == "follow":
current_user_profile.follows.add(profile)
elif action == "unfollow":
current_user_profile.follows.remove(profile)
current_user_profile.save()
return render(request, "inbox/profile.html", {"profile": profile})
profile.html:
<!-- inbox/templates/inbox/profile_list.html -->
{% extends 'inbox/layout.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
<div >
<p>#{{ profile.user.username|upper }}</p>
<div class="buttons has-addons">
{% if profile in user.profile.follows.all %}
<button class="button is-success is-static">Follow</button>
<button class="button is-danger" name="follow" value="unfollow">
Unfollow
</button>
{% else %}
<button class="button is-success" name="follow" value="follow">
Follow
</button>
<button class="button is-danger is-static">Unfollow</button>
{% endif %}
</div>
<p1>following:</p1>
<ul>
{% for following in profile.follows.all %}
<li>{{ following }}</li>
{% endfor %}
</ul>
<p2>followers:</p2>
<ul>
{% for follower in profile.followed_by.all %}
<li>{{ follower }}</li>
{% endfor %}
</ul>
</div>
</form>
{% endblock content %}
profile_list.html template:
<!-- inbox/templates/inbox/profile_list.html -->
{% extends 'inbox/layout.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
{% for profile in profiles %}
<div class="column">
<p>{{ profile.user.username }}</p>
<p>#{{ profile.user.username|lower }}</p>
</div>
{% endfor %}
</form>
{% endblock content %}
You need to match Profile's pk with profile = Profile.objects.get(pk=pk), currently you mentioned the pk of User model, which matched the given queryset in profile view, so try sending pk of profile in profile_list.html template:
{% for profile in profiles %}
<div class="column">
<p>{{ profile.user.username }}</p>
<p>#{{ profile.user.username|lower }}</p>
</div>
{% endfor %}
Also, it is better to use get_object_or_404 instead of get, as it calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.
So, views.py:
def profile(request, pk):
if not hasattr(request.user, 'profile'):
missing_profile = Profile(user=request.user)
missing_profile.save()
profile = get_object_or_404(Profile, pk=pk)
if request.method == "POST":
current_user_profile = request.user.profile
data = request.POST
action = data.get("follow")
if action == "follow":
current_user_profile.follows.add(profile)
elif action == "unfollow":
current_user_profile.follows.remove(profile)
current_user_profile.save()
return render(request, "home/profile.html", {"profile": profile})
Now, if the profile does not exist, it will show no Profile matches the given query.
I'm coming to you because I have a problem with my form. Let me explain, when I enter the information in the inputs and I click on the button, it does absolutely nothing, without any error message.
This is views.py:
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect
from .models import Movies
from .forms import CreateUserForm
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout
from django.conf import settings
from django.contrib.auth.decorators import login_required
# Create your views here.
def home(request):
context = {
'movies': Movies.objects.all()
}
return render(request, 'list/home.html', context)
#login_required(login_url='login-page')
def add(request):
return render(request, 'list/add.html', {'title': 'Add Movies'})
def signup(request):
if request.method == 'POST':
form = CreateUserForm(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect('list-home')
else:
form = CreateUserForm()
return render(request, 'list/sign.html', {'form': form})
This is forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class CreateUserForm(UserCreationForm):
username = forms.CharField(max_length=50, required=True, widget=forms.TextInput(attrs={"placeholder": "Your pseudo:"}))
first_name = forms.CharField(max_length=50, required=True, widget=forms.TextInput(attrs={"placeholder": ">Your first name:"}))
last_name = forms.CharField(max_length=50, required=True, widget=forms.TextInput(attrs={"placeholder": "Your last name:"}))
email = forms.EmailField( max_length=50, required=True, widget=forms.TextInput(attrs={"placeholder": "Your email:"}))
password = forms.CharField(max_length=50, required=True, widget=forms.PasswordInput(attrs={"placeholder": "Your password:"}))
password2 = forms.CharField(max_length=50, required=True, widget=forms.PasswordInput(attrs={"placeholder": "Confirm your password:"}))
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password', 'password2')
And my sign.html:
{% extends "list/base.html" %}
{% block content %}
<main class="site-main" style="background-color: black; color: white;">
<div class="form-box2">
<h1 style="text-align: center; padding: 20px;">SIGN UP</h1>
<form method="POST" class="conta" style="text-align: center;">
{% csrf_token %}
{{form.username}}
{{form.first_name}}
{{form.last_name}}
{{form.email}}
{{form.password}}
{{form.password2}}
<button type="submit">Sign up</button>
</form>
{% if messages%}
{% for message in messages%}
<div class="alert alert-{{message.tags }}">{{ message }}</div>
{% endfor %}
{% endif %}
</div>
</main>
{% endblock content%}
So here it is, I probably forgot something but I can't find it, I hope you can help me. Thank you in advance for your help.
You're creating a new form if the form is invalid, so there won't be any errors in that new form.
def signup(request):
form = CreateUserForm() # Form for GET request
if request.method == 'POST':
form = CreateUserForm(request.POST) # add the data from POST to the form
if form.is_valid():
form.save(commit=True)
return redirect('list-home')
# An invalid form will end up here with the errors in it.
return render(request, 'list/sign.html', {'form': form})
Looking at your template, because you're just rendering each field you also won't see the errors attached to a field.
While you're getting this up & running, try to get Django to do the most work for you. There may also be other errors, so you should include those if present;
{% if form.non_field_errors %}
<ul>
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{# To really make sure you debug this #}
{{ form.errors }}
<form method="POST" class="conta" style="text-align: center;">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Sign up</button>
</form>
the code is working without any issues but the problem is that after I like a post instead of the like button changing the entire for loop having the posts stops functioning. ie there is nothing displayed other than the users username. The server doesn't throw any particular error it just doesn't display the for loop. I feel its an issue with the render part but I'm not quite sure what exactly is wrong with it
below are my files
views.py
from django.shortcuts import render,get_object_or_404
from django.views.generic import ListView
from .models import Blog
from django.http import HttpResponseRedirect
class BlogsList(ListView):
model=Blog
template_name='blog/home.html'
context_object_name='blogs'
ordering=['-date_posted']
def like_post(request, blog_id):
post = get_object_or_404(Blog, id=blog_id)
is_liked=False
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
is_liked=False
else:
post.likes.add(request.user)
is_liked=True
context={
'is_liked':is_liked
}
return render(request, 'blog/home.html', context)
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 Blog(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)
likes=models.ManyToManyField(User,related_name='likes',blank=True)
def __str__(self):
return self.title
def get_absolute_url():
return reverse('blog-home')
urls.py
from django.urls import path
from . import views
urlpatterns=[
path('',views.BlogsList.as_view(),name='blog-home'),
path('<int:blog_id>/like/', views.like_post, name='like_post')
]
and home.html
{{ user.username }}
{% block content %}
{% for post in blogs %}
<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">
<h2>{{ post.author }}</h2>
<small class="text-muted">{{ post.date_posted|date:"F d, Y" }}</small>
</div>
<h2>{{ post.title }}</h2>
<p class="article-content">{{ post.content }}</p>
</div>
<div>
<form action="{% url 'like_post' post.id %}">
{% csrf_token %}
{% if is_liked %}
<button type='submit' name='blog_id' value="{{ post.id }}" class="btn btn-danger">Unlike</button>
{% else %}
<button type='submit' name='blog_id' value="{{ post.id }}" class="btn btn-primary">Like</button>
{% endif %}
</form>
</div>
</article>
{% endfor %}
{% endblock %}
you can directly use the redirect shortcut instead of render because render will not going to show all the post. You can either pass all the post in like view or simply call the list view by redirecting it.
from django.shortcuts import redirect
def like_post(request, blog_id):
# rest of code
return redirect(post.get_absolute_url()) # redirect will going to call the BlogsList and your all post will be rendered
When you click like button, your code is rendering from like_post view. In that view, context has no data called blogs. at the end of like_post, you should redirect to your BlogsListView instead of returning a HttpResponse.
you can use a django shortcut function redirect to do this.
from django.shortcuts import redirect
#...
#...
#... your code
def like_post(request, blog_id):
post = get_object_or_404(Blog, id=blog_id)
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
else:
post.likes.add(request.user)
return redirect('blog-home')
Also, you need to figure out some way to get is_liked context in home itself. consider checking documentation here
I am going through a Django tutorial and getting this error when trying to edit posts in my blog app. I use Django Version: 2.0.6 and Python Version: 3.6.5.
models.py
from django.db import models
class BlogPost(models.Model):
title = models.CharField(max_length=100)
text = models.TextField()
def __str__(self):
return self.title
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^new_post/', views.new_post, name='new_post'),
url(r'^edit_post/(?P<post_id>\d+)/$', views.edit_post, name='edit_post'),
]
A template that causes the error in line 3 - edit_post.html. The error message highlights {% url 'edit_post' post.id %}
{% block content %}
<form action="{% url 'edit_post' post.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">save changes</button>
</form>
{% endblock content %}
A template (index.html) with a link to edit_post.html
{% block content %}
<form action="{% url 'new_post' %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Make a new post</button>
</form>
<ul>
{% for post in posts %}
<li>
{{ post.id }} - {{ post.title }}
<p>{{ post.text }}</p>
edit post
</li>
{% empty %}
<li>No posts have been added yet.</li>
{% endfor %}
</ul>
{% endblock content %}
views.py
def edit_post(request, post_id):
post = BlogPost.objects.get(id=post_id)
text = post.text
title = post.title
if request.method != 'POST':
form = BlogForm(instance=post)
else:
form = BlogForm(instance=post, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('index'))
context = {'title': title, 'text': text, 'form': form}
return render(request, 'blog/edit_post.html', context)
forms.py
from django import forms
from .models import BlogPost
class BlogForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['title', 'text']
The problem
When I click edit post link on index page, I get the aforementioned error. Creating a new post using this approach works flawlessly but editing does not.
I am stuck with this problem and have no idea what is wrong.
What I have tried
I have tried replacing django.conf.urls.url with django.urls.path and the patterns correspondingly.
I have change a link to a button.
I have tried
I have read What is a NoReverseMatch error, and how do I fix it?
on StackOverflow and as many topics as I could find.
Any help is appreciated. Thank you in advance!
in your blog/edit_post.html you use post.id
<form action="{% url 'edit_post' post.id %}" method='post'>
...
</form>
but in views.py you don't pass post variable to context variable
def edit_post(request, post_id):
post = BlogPost.objects.get(id=post_id)
...
context = {
'title': title,
'text': text,
'form': form,
'post': post # here
}
return render(request, 'blog/edit_post.html', context)
i have an app called reviews
reviews/forms.py
from django.forms import ModelForm, Textarea
from reviews.models import Review
class ReviewForm(ModelForm):
class Meta:
model = Review
fields = ['rating', 'comment']
widgets = {
'comment': Textarea(attrs={'cols': 40, 'rows': 15}),
}
reviews/views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from .models import Review, Wine
from .forms import ReviewForm
import datetime
from django.contrib.auth.decorators import login_required
#login_required
def add_review(request, wine_id):
wine = get_object_or_404(Wine, pk=wine_id)
form = ReviewForm(request.POST)
if form.is_valid():
rating = form.cleaned_data['rating']
comment = form.cleaned_data['comment']
user_name = form.cleaned_data['user_name']
user_name = request.user.username
review = Review()
review.wine = wine
review.user_name = user_name
review.rating = rating
review.comment = comment
review.pub_date = datetime.datetime.now()
review.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('reviews:wine_detail', args=(wine.id,)))
return render(request, 'reviews/wine_detail.html', {'wine': wine, 'form': form})
reviews/templates/reviews/wine_detail.html
{% extends 'base.html' %}
{% load bootstrap3 %}
{% block title %}
<h2>{{ wine.name }}</h2>
<h5>{{ wine.review_set.count }} reviews ({{ wine.average_rating | floatformat }} average rating)</h5>
{% endblock %}
{% block content %}
<h3>Recent reviews</h3>
{% if wine.review_set.all %}
<div class="row">
{% for review in wine.review_set.all %}
<div class="col-xs-6 col-lg-4">
<em>{{ review.comment }}</em>
<h6>Rated {{ review.rating }} of 5 by {{ review.user_name }}</h6>
<h5><a href="{% url 'reviews:review_detail' review.id %}">
Read more
</a></h5>
</div>
{% endfor %}
</div>
{% else %}
<p>No reviews for this wine yet</p>
{% endif %}
<h3>Add your review</h3>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'reviews:add_review' wine.id %}" method="post" class="form">
{% csrf_token %}
{% bootstrap_form form layout='inline' %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "star" %} Add
</button>
{% endbuttons %}
</form>
{% endblock %}
base.html
{% load bootstrap3 %}
{% bootstrap_css %}
{% bootstrap_javascript %}
{% block bootstrap3_content %}
<div class="container">
<nav class="navbar navbar-default">
<div class="navbar-header">
<a class="navbar-brand" href="{% url 'reviews:review_list' %}">Winerama</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>Wine list</li>
<li>Home</li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %}
<li>Hello {{ user.username }}</li>
<li>Logout</li>
{% else %}
<li>Login</li>
<li>Register</li>
{% endif %}
</ul>
</div>
</nav>
<h1>{% block title %}(no title){% endblock %}</h1>
{% bootstrap_messages %}
{% block content %}(no content){% endblock %}
</div>
{% endblock %}
I am getting the error at the line {% bootstrap_form form layout='inline' %} in the html file
Any idea how to fix this?
There's a few problems with your code as it stands, so I'll try to clean it up with some comments as I would write it to add a review to a wine.
#login_required
def add_review(request, wine_id):
wine = get_object_or_404(Wine, pk=wine_id)
if request.POST:
form = ReviewForm(request.POST)
else:
form = ReviewForm()
if form.is_valid():
### NO NEED FOR - already set as part of valid modelform ::: rating = form.cleaned_data['rating']
### AS WELL AS ::: comment = form.cleaned_data['comment']
### THIS IS NOT A FIELD IN YOUR FORM :::user_name = form.cleaned_data['user_name']
user_name = request.user.username
review = form.save(commit=False) # commit = False means that this instantiate but not save a Review model object
review.wine = wine
review.user_name = user_name # Why use this instead of a ForeignKey to user?
review.pub_date = datetime.datetime.now() # works as long as pub_date is a DateTimeField
review.save() # save to the DB now
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('reviews:wine_detail', args=(wine.id,))) # THIS will redirect only upon form save
return render(request, 'reviews/wine_detail.html', {'wine': wine, 'form': form})
Now, the error your seeing is most likely related to you passing request.POST to a form even if request.POST is blank; the form will attempt to set initial values but with a querydict that has no values that actually relates to the form.
EDIT: In response to your comments, my next step would be to try and render each form field individually and see if I can trigger a failure.
Instead of {% bootstrap_form form layout='inline' %}, try-
{% for field in form %}
{% bootstrap_field field %}
{% endfor %}
If this is an error with the django-bootstrap library trying to render the textarea widget and the inline style together (as I would suspect at this point), you can also eliminate the widget parameter and see if there's a fix. If there is, I'd suggest overriding your modelform's init method for assign a widget post a call super on init.
In Class Base View
This error may occur when you use form_class in the wrong generic view.
⮕ Open your views.py then check to see if you have set the wrong generic view in your class.
Example
class ProfileUpdateView(T̶e̶m̶p̶l̶a̶t̶e̶V̶i̶e̶w̶ UpdateView):
model = User
form_class = forms.ProfileForm
success_url = reverse_lazy("stories:story_list")
template_name = 'profile.html'
def get_object(self, queryset=None):
return get_object_or_404(User, pk=self.request.user.id)