The 'header_image' attribute has no file associated with it - python

When I create a new post with an image, everything is fine, but if I edit it, I want to delete the image using the "clear" button, then this error appears, and if I change, then nothing changes, but there are no errors
here is models.py
`
from django.db import models
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
body = models.TextField()
header_image = models.ImageField(blank=True, null=True, upload_to="images/", default='#') #new
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', args=[str(self.id)])`
here is views.py
`
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Post
class BlogListView(ListView):
model = Post
template_name = 'home.html'
class BlogDetailView(DetailView):
model = Post
template_name = 'post_detail.html'
class BlogCreateView(CreateView):
model = Post
template_name = 'post_new.html'
fields = ['title', 'author', 'body', 'header_image']
class BlogUpdateView(UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body', 'header_image']
class BlogDeleteView(DeleteView):
model = Post
template_name = 'post_delete.html'
success_url = reverse_lazy('home')
#property
def image_url(self):
"""
Return self.photo.url if self.photo is not None,
'url' exist and has a value, else, return None.
"""
if self.image:
return getattr(self.photo, 'url', None)
return None`
post_base.html
`{% load static %}
<html>
<head>
<title>Django blog</title>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400"
rel="stylesheet">
<link href="{% static 'css/base.css' %}" rel="stylesheet">
</head>
<body>
<div>
<header>
<div class="nav-left">
<h1>Django blog</h1>
<h2>Admin</h2>
</div>
<div class="nav-right">
+ New Blog Post
</div>
</header>
{% if user.is_authenticated %}
<p>Hi {{ user.username }}!</p>
{% else %}
<p>You are not logged in.</p>
Log In<br>
<p>Sign up</p>
{% endif %}
{% block content %}
{% endblock content %}
</div>
</body>
</html>`
post_detail.html
`
{% extends 'base.html' %}
{% block content %}
<div class="post-entry">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
<p>+ Edit Blog Post</p>
<p>+ Delete Blog Post</p>
<img src="{{ post.header_image.url|default_if_none:'#' }}">
{{ post.body|safe }}
{% endblock content %}`
post_new.html
`
{% extends 'base.html' %}
{% block content %}
<h1>New post</h1>
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
{% endblock content %}`
post_edit.html
`
{% extends 'base.html' %}
{% block content %}
<h1>Edit post</h1>
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update" />
</form>
{% endblock content %}`

enctype='multipart/form-data' means that no characters will be encoded. that is why this type is used while uploading files to server. So multipart/form-data is used when a form requires binary data, like the contents of a file, to be uploaded.
You forgot to add enctype='multipart/form-data' in your post_edit.html form and that's the reason your files aren't sent to Django. Following code should work.
post_edit.html
{% extends 'base.html' %}
{% block content %}
<h1>Edit post</h1>
<form action="" method="post" enctype='multipart/form-data'>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update" />
</form>
{% endblock content %}

Related

How do I link two views to one template file in Django

I'm trying to build a very basic messaging app where someone types into a text input, presses send then see's the message on the screen. And I want to do all of this on the same URL. Here is what I have right now:
views.py:
from django.shortcuts import render
from django.views.generic import ListView, CreateView
from message import models
# Create your views here.
class ViewMessages(ListView):
model = models.Messages
context_object_name = 'messages'
class WriteMessages(CreateView):
fields = ('message',)
model = models.Messages
models.py:
from django.db import models
from django import forms
from django.core.urlresolvers import reverse
# Create your models here.
class Messages(models.Model):
message = models.CharField(max_length=300)
def __str__(self):
return self.message
def get_absolute_url(self):
return reverse("view")
project urls.py:
from django.conf.urls import url
from django.contrib import admin
from message import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^view/', views.ViewMessages.as_view(), name="view"),
url(r'^create/', views.WriteMessages.as_view(), name="create"),
]
messages_form.html
{% extends "message/base.html" %}
{% block head_block %}
<title>Create Message</title>
{% endblock %}
{% block body_block %}
{% for message in messages %}
<h3><div class="text-center"><span class="label label-default">{{ message.message }}</span></div></h3>
{% endfor %}
<form method="POST" class="form-horizontal">
{% csrf_token %}
<div class="text-center" style="position: fixed; top: 500px;">
<span style="margin: 10px;">{{ form }}</span>
<br>
<input type="submit" value="Send" class="btn btn-primary btn-group btn-group-lg" >
</div>
</form>
{% endblock %}
messages_list.html:
<!DOCTYPE html>
{% extends "message/base.html" %}
{% block head_block %}
<title>Read Messages</title>
{% endblock %}
{% block body_block %}
<div class="">
{% for message in messages %}
<h3><div class="text-center"><span class="label label-default">{{ message.message }}</span></div></h3>
{% endfor %}
<br>
<p><div class="text-center"><a style="border: 1px solid gray;" class="btn btn-success" href="{% url 'create' %}">Write Message</a></div></p>
</div>
{% endblock %}
The basic idea is that I want to combine the "messages_form.html" and "messages_list.html" into one template file called "message.html".
Can anyone help me?
I had something similar and here was my approach
views.py
from django.views import View
class MessageView(View):
template_name = 'message.html'
queryset = Messages.objects.all()
def get_queryset(self):
return self.queryset
def get(self, request, *args, **kwargs):
form = MessageForm()
context = {
'object_list': self.get_queryset(),
"form": form
}
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
form = MessageForm(request.POST)
if form.is_valid():
form.save()
form = MessageForm()
return redirect('/message')
context = {
'object_list': self.get_queryset(),
"form": form
}
return render(request, self.template_name, context)
message.html
{% extends "message/base.html" %}
{% block head_block %}
<title>Message</title>
{% endblock %}
{% block body_block %}
{% for message in object_list %}
<h3><div class="text-center"><span class="label label-default">{{ message.message }}</span></div></h3>
{% endfor %}
<form method="POST" class="form-horizontal">
{% csrf_token %}
<div class="text-center" style="position: fixed; top: 500px;">
<span style="margin: 10px;">{{ form }}</span>
<br>
<input type="submit" value="Send" class="btn btn-primary btn-group btn-group-lg" >
</div>
</form>
{% endblock %}
Display your view in a url
urls.py
from django.urls import path
from .views import MessageView
urlpatterns = [
path('message', MessageView.as_view(), name='messages'),
]

Display likers username in html template

I have added a user liking system, where a user can like posts. I have added this successfully, however can't seem to display the usernames of the people who liked the post. This is my current implementation.
models.py
class Post(models.Model):
likes = models.ManyToManyField(User, related_name='likes')
#property
def total_likes(self):
return self.likes.count()
views.py
def likepost(request, pk):
if request.method == 'POST':
user = request.user
post = get_object_or_404(Post, pk=pk)
if post.likes.filter(id=user.id).exists():
post.is_liked = True
post.likes.remove(user)
post.save()
else:
post.is_liked = False
post.likes.add(user)
post.save()
return redirect('home')
home.html
{% for post in posts.all %}
<form id="like__post" method="POST" action="{% url 'likepost' post.id %}">
{% csrf_token%}
<input type="hidden">
</form>
<div style="position: absolute; bottom:80px; left: 85px;">
{% if post.is_liked == True %}
<img src="{% static 'heartred.png' %}" width="35px" height="35px">
{% else %}
<img src="{% static 'heart.png' %}" width="33px" height="33px">
{% endif %}
{% endfor %}
<ul> {{ post.likes }} </ul>
So far. In my template i get this: auth.User.None
likes is a many to many field, you need to iterate over it.
<ul>
{% for like in post.likes.all %}
<li>{{ like.username }}</li>
{% endfor %}
</ul>

Nothing is happened when I put comment button

Nothing is happened when I put comment button.I wanna make a page which is shown comment&recomment.I wrote codes in views.py
class DetailView(generic.DetailView):
model = POST
template_name = 'detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comment_form'] = CommentCreateForm()
context['recomment_form'] = ReCommentCreateForm()
return context
class CommentCreateView(generic.View):
def post(self, request, *args, **kwargs):
form = CommentCreateForm(request.POST)
post = POST.objects.get(pk=kwargs['post_pk'])
if form.is_valid():
obj = form.save(commit=False)
obj.target = post
obj.save()
return redirect('detail', pk=post.pk)
class ReCommentCreateView(generic.View):
def post(self, request, *args, **kwargs):
form = ReCommentCreateForm(request.POST)
comment = Comment.objects.get(pk=kwargs['comment_pk'])
if form.is_valid():
obj = form.save(commit=False)
obj.target = comment
obj.save()
return redirect('detail', pk=comment.target.pk)
in urls.py
from django.urls import path
from django.conf import settings
from . import views
urlpatterns = [
path('detail/<int:pk>/', views.DetailView.as_view(), name='detail'),
path('comment/<int:post_pk>/',views.CommentCreateView.as_view(), name='comment'),
path('recomment/<int:comment_pk>/', views.ReCommentCreateView.as_view(), name='recomment'),
]
in detail.html
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DETAIL</title>
</head>
<body>
<div id="comment-area">
{% for comment in post.comment.all %}
<div class="media m-3">
<div class="media-body">
<h5 class="mt-0">
<span class="badge badge-primary badge-pill">{% by_the_time comment.created_at %}</span>
{{ comment.name }}
<span class="lead text-muted">{{ comment.created_at }}</span>
Recomment
</h5>
{{ comment.text | linebreaksbr }}
{% for recomment in comment.recomment.all %}
<div class="media m-3">
<div class="media-body">
<h5 class="mt-0">
{{ recomment.name }}
<span class="lead text-muted">{{ recomment.created_at }}</span>
</h5>
{{ recomment.text | linebreaksbr }}
</div>
</div>
{% endfor %}
<form action="{% url 'recomment' comment_pk=comment.id %}" method="post">
{{recomment_form}}
{% csrf_token %}
<input class="btn btn-primary" type="submit" value="re-comment">
</form>
</div>
</div>
{% endfor %}
<form action="{% url 'comment' post_pk=post.id %}" method="post">
{{comment_form}}
{% csrf_token %}
<input class="btn btn-primary" type="submit" value="comment">
</form>
</div>
</body>
</html>
in models.py
class Comment(models.Model):
name = models.CharField(max_length=100, blank=True)
text = models.TextField()
target = models.ForeignKey(POST, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
When I put comment button, no error happens but same page which is form's content is blank is shown.I wanna show {% for comment in post.comment.all %} ~ {% endfor %},so I really cannot understand why I cannot do it.I think values are not in model, so I print out print(obj) in CommentCreateView's class, and correctly value is shown in terminal.What is wrong in my code?How should I fix this?
Do you have the objects actually created in the DB? There's some info missing in the post (like the Form itself, etc), anyway:
If you didn't set the related_name argument on the foreign key, the correct access to post's comments is via post.comment_set.all(), see:
https://docs.djangoproject.com/en/2.0/topics/db/queries/#following-relationships-backward
May I point out that you're doing a lot of work, that Django's generic views can do for you (guess it's OK if you're learning, otherwise, it's just more code to maintain).
Things like CreateView, UpdateView, and DeleteView from django.views.generic.edit. For more info see:
https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#model-forms

django - csrf protection - markdown2

In some of my blog posts using django and markdown2, I am trying to include a form as follows:
views.py:
def post_detail(request, slug=None):
instance = get_object_or_404(Post, slug=slug)
if not instance.published:
raise Http404
return render(request, "posts/post_detail.html", {'instance': instance})
My template post_detail.html contains a {{ instance.get_markdown }} variable:
{% extends "base.html" %}
{% block body_content %}
<div>
{{ instance.get_markdown }}
</div>
{% endblock body_content %}
base.html:
<html>
<body>
{% block body_content %}
{% endblock body_content %}
</body>
</html>
models.py:
import markdown2
class Post(models.Model):
...
text = models.TextField(verbose_name=_("Text"))
...
def get_markdown(self):
return mark_safe(markdown2.markdown(self.text))
Example for a saved text in Post:
### Example
<form method = "POST">
{% csrf_token %}
First name:<input type="text" name="firstname">
<input type="submit" value="Submit">
</form>
This way, the page simply shows the string "{% csrf_token %}" within the form. How can I render the post with csrf protection?
My temporary solution is:
post_detail.html:
{% extends "base.html" %}
{% block body_content %}
<div>
{{ instance.get_markdown_text }}
</div>
{% if instance.form %}
<div>
<form method = "POST">{% csrf_token %}
{{ instance.get_markdown_form }}
</form>
</div>
{% endif %}
{% endblock body_content %}
models.py:
import markdown2
class Post(models.Model):
...
text = models.TextField(verbose_name=_("Text"))
form = models.TextField(verbose_name=_("Form"), blank=True, null=True)
...
def get_markdown_text(self):
return mark_safe(markdown2.markdown(self.text))
def get_markdown_form(self):
return mark_safe(markdown2.markdown(self.form))
I'm not very happy with this solution, because of the unnecessary field "form" and method get_markdown_form in Post and post_detail.html.

BootstrapError Parameter "form" should contain a valid Django Form

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)

Categories

Resources