I'm fairly new to coding in general, so please forgive my ignorance.
I have a ModelForm (Django 1.10 -just in case-) for a 'post' on a social network website:
models.py:
class Post(models.Model):
user = models.ForeignKey(User)
text = models.TextField()
image = models.ImageField(blank=True)
draft = models.BooleanField(default=False)
submission_date = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.text
forms.py:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['text', 'image', 'draft']
To update a post, this is its function:
views.py:
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if not request.user.is_superuser and\
not request.user == post.user:
raise PermissionDenied
if request.method == 'POST':
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
context = {'post': post,'form': form}
return HttpResponse(render(request, 'accounts/view_post.html', context))
elif request.method == 'GET':
form = PostForm(request.GET, instance=post)
context = {'post': post,'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
in the template:
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'accounts/form_template.html' %}
<input class="btn btn-btn-success" type="submit" value="Save Post">
</form>
My question is: when trying to update, why does the original field input not show up? The fields turn up empty as if I were creating a new post.
The more detailed answer, the more I would appreciate it.
Thank you in advance.
Turns out all I needed to do was remove 'request.GET':
in views.py:
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
...
...
elif request.method == 'GET':
form = PostForm(instance=post)
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
Thank you to everyone who tried to help, especially #Zagorodniy Olexiy.
If you want to show original inputs in the form you have to ad some code in your PostForm:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['text', 'image', 'draft']
def __init__(self, *args, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
then you have to load it in to the view
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if not request.user.is_superuser and\
not request.user == post.user:
raise PermissionDenied
if request.method == 'POST':
form = PostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/view_post.html', context))
elif request.method == 'GET':
form = PostForm(request.GET, instance=post)
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
Related
Would like to know if it's possible to prepopulate my CommentForm in UpdateView. Updating comments work's except that the form is not loaded prepopulated.
When testing using a separate template it's loaded prepopulated, but I would like to use the same template (PostDetail) using a modal to update the comment.
views.py:
class PostDetail(View):
def get(self, request, slug, pk, *args, **kwargs):
queryset = Post.objects.all()
post = get_object_or_404(queryset,slug=slug, pk=pk)
comments = post.comments.order_by('-created_on')
return render(
request,
'blog/post_detail.html',
{
'post': post,
'comments': comments,
'comment_form': CommentForm()
},
)
def post(self, request, slug, pk, *args, **kwargs):
if request.user.is_authenticated:
queryset = Post.objects.all()
post = get_object_or_404(queryset, slug=slug, pk=pk)
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.post = post
comment.author = request.user
comment.save()
messages.info(request, 'Comment added')
return HttpResponseRedirect(reverse('post_detail', args=[slug, pk]))
class CommentUpdate(LoginRequiredMixin, UserPassesTestMixin, generic.UpdateView):
model = Comment
template_name = 'blog/post_detail.html'
form_class = CommentForm
def get_success_url(self):
post = Post.objects.get(pk=self.object.post.pk)
messages.info(self.request, 'Comment updated')
return post.get_absolute_url()
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
comment = self.get_object()
if self.request.user == comment.author:
return True
return False`
forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body',)
post_detail.html (form):
<form action="{% url 'comment_update' post.slug comment.pk %}" method="POST">
{% csrf_token %}
{{ comment_form | crispy }}
<button type="submit" class="btn">Update</button>
</form>
Please need help :)
I have tested adding:
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['instance'] = self.get_object()
return kwargs
The form is still not prepopulated. It works if I use a separate template and render the form using {{ form | crispy }}.
Perhaps it's because I have two {{ comment_form | crispy }} in my post_detail.html, one when creating the comment and one to update it?
I've spend alot of time trying to figure this out :)
You can pass that instance of the Comment model which you'd like to update in CommetForm. In the get_form_kwargs() method, you can add the instance keyword argument to the form kwargs with the instance of the Comment model to update. Then, the CommentForm will be pre-populated with the data from that instance while displaying in frontend so:
class CommentUpdate(LoginRequiredMixin, UserPassesTestMixin, generic.UpdateView):
model = Comment
template_name = 'blog/post_detail.html'
form_class = CommentForm
def get_success_url(self):
post = Post.objects.get(pk=self.object.post.pk)
messages.info(self.request, 'Comment updated')
return post.get_absolute_url()
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['instance'] = self.get_object()
return kwargs
def test_func(self):
comment = self.get_object()
if self.request.user == comment.author:
return True
return False
I'm trying to create a blog model but the form data is not being saved in the database after submitting the form.
views.py
def postsform(request):
if request.method == "POST":
form = BlogForm(request.POST)
if form.is_valid():
form.save()
return redirect('blog')
else:
form = BlogForm()
messages.warning(request, "Opps! Something went wrong.")
return render(request, 'blog/postform.html', {'form':form})
else:
form = BlogForm()
return render(request, 'blog/postform.html', {'form':form})
forms.py
from django_summernote.widgets import SummernoteWidget
class BlogForm(ModelForm):
class Meta:
model = BlogPost
widgets = {
'blog': SummernoteWidget(),
}
fields = ['title', 'featureImg', 'blog', 'meta_description', 'keyword', 'author']
models.py
class BlogPost(models.Model):
title = models.CharField(max_length=999)
featureImg = ProcessedImageField(upload_to = 'blog/', format='JPEG',options={'quality':60}, null=True)
slug = models.CharField(max_length=999, blank=True,null= True)
blog = models.TextField()
meta_description = models.TextField()
keyword = models.TextField()
author = models.CharField(max_length=255)
created_on = models.DateField(auto_now_add=True)
updated_on = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if BlogPost.objects.filter(title=self.title).exists():
extra = str(randint(1, 1000000))
self.slug = slugify(self.title) + "-" + extra
else:
self.slug = slugify(self.title)
super(BlogPost, self).save(*args, **kwargs)
html
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Publish</button>
</form>
I've tried finding where I made the mistake but couldn't find it. After submitting the form the warning message pops up and the form doesn't get submitted.
You should not construct a new form, since then it will not render the errors. Likely you did not pass request.FILES, and the enctype="…" [mdn-doc] is also missing in the <form> tag:
def postsform(request):
if request.method == 'POST':
form = BlogForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('blog')
else:
# no new BlogForm
messages.warning(request, 'Oops! Something went wrong.')
else:
form = BlogForm()
return render(request, 'blog/postform.html', {'form': form})
and in the HTML form:
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Publish</button>
</form>
I'm currently learning the Python / Django stack by following some training to build a blog.
I currently have two similar views for adding new and editing existing posts (post_new and post_edit) as below:
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect("post_detail", pk=post.pk)
else:
form = PostForm()
return render(request, "blog/post_edit.html", {"form": form})
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect("post_detail", pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, "blog/post_edit.html", {"form":form})
Although these views do different jobs they share some identical code.
Trying to follow best practice (DRY), is there a sensible way to make such similar views DRYer? Or is it better to leave views of this sort of length in long form to keep them easy to read?
I would personally write it like this:
def post_edit(request, pk=None):
if pk is not None:
post = get_object_or_404(Post, pk=pk)
else:
post = None
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect("post_detail", pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, "blog/post_edit.html", {"form":form})
Basically, you pass the default instance value to the ModelForm.
You probably want to use Class-based views for that.
from django.views.generic.edit import CreateView, UpdateView
class PostCreate(CreateView):
model = Post
fields = ['name', ...]
class PostUpdate(UpdateView):
model = Post
fields = ['name', ...]
I have followed Django Girls tuorial http://tutorial.djangogirls.org/en/index.html and successfully created a blog. However I wanted to add an image field to my block,
Currently my models.py looks like:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length = 200)
text = models.TextField()
model_pic= models.ImageField(upload_to = 'blog/images', default='blog/images/already.png')
created_date = models.DateTimeField(default = timezone.now)
published_date = models.DateTimeField(blank = True, null =True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
def approved_comment(self):
return self.comments.filter(approved_comment=True)
class Comment(models.Model):
post = models.ForeignKey('blog.Post', related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
and my views.py looks like:
from django.shortcuts import render, get_object_or_404 ,redirect
from .models import Post, Comment
from django.utils import timezone
from .forms import PostForm, CommentForm
from django.contrib.auth.decorators import login_required
def post_list(request):
posts = Post.objects.filter(published_date__lte = timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html',{'posts' : posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk = pk)
return render(request, 'blog/post_detail.html',{'post':post})
#login_required
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit = False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
#login_required
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_edit.html', {'form': form})
#login_required
def post_draft_list(request):
posts = Post.objects.filter(published_date__isnull=True).order_by('created_date')
return render(request, 'blog/post_draft_list.html',{'posts':posts})
#login_required
def post_publish(request, pk):
post = get_object_or_404(Post, pk=pk)
post.publish()
return redirect('blog.views.post_detail', pk=pk)
#login_required
def post_remove(request, pk):
post = get_object_or_404(Post, pk=pk)
post.delete()
return redirect('blog.views.post_list')
def add_comment_to_post(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit = False)
comment.post = post
comment.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = CommentForm()
return render(request,'blog/add_comment_to_post.html', {'form': form})
#login_required
def comment_approve(request, pk):
comment = get_object_or_404(Comment, pk=pk)
comment.approve()
return redirect('blog.views.post_detail', pk=comment.post.pk)
#login_required
def comment_remove(request, pk):
comment = get_object_or_404(Comment, pk=pk)
post_pk = comment.post.pk
comment.delete()
return redirect('blog.views.post_detail', pk=post_pk)
and my forms.py appears like :
from django import forms
from .models import Post, Comment
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text')
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text',)
Now Please guide me what changes should I make in model Post and how should I modify the view to get my image rendered and how should I make the template for same
I tried https://coderwall.com/p/bz0sng/simple-django-image-upload-to-model-imagefield but It didn't worked for me.
Any help will be highly appreciated.
Thank You.
Had you need any more information please comment it.
So, let's go.
When you set a ImageField(), you have a upload_to attribute. From the documentation:
A local filesystem path that will be appended to your MEDIA_ROOT setting to determine the value of the url attribute.
So you will have a function to determine a dynamic path where the image will be stored (read the link above for more information).
E.g.
class Post(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length = 200)
text = models.TextField()
model_pic= models.ImageField(upload_to=upload_image, default='blog/images/already.png')
created_date = models.DateTimeField(default = timezone.now)
published_date = models.DateTimeField(blank = True, null =True)
def upload_image(self, filename):
return 'post/{}/{}'.format(self.title, filename)
Note that you don't include the model_pic in your fields of the PostForm. So it will not appear on the template.
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text', 'model_pic',)
Your view will change a little bit:
#login_required
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST, request.FILES)
Once you're using an upload, you will use the request.FILES read more here, but let me quote this:
Note that FILES will only contain data if the request method was POST and the that posted to the request had enctype="multipart/form-data". Otherwise, FILES will be a blank dictionary-like object.
So on your template, your tag <form> will have a attribute enctype="multipart/form-data"
<form enctype="multipart/form-data" method="POST" action="">
{{ form.as_p }}
</form>
After all, you can retrieve this image to show in a page. Assuming that you have passed a post instance via context to the template:
<img src="{{ post.model_pic.url }}" alt="{{ post.title }}" />
I am trying to create a ModelForm that updates a record in my database, but for some reason it isn't working. It turns out blank. What am I doing wrong here?
this is my forms.py:
class PageForm(ModelForm):
class Meta:
model = Page
this is my views.py:
def detail(request, page_id):
p = get_object_or_404(Page, pk=id)
if request.method == 'POST':
form = PageForm(request.POST, instance=p)
if form.is_valid():
form.save()
messages.success(request, "Detail updated successfully.")
return HttpResponseRedirect('/thanks/')
return render(request, 'pages/pageform.html', {
'form': PageForm(instance=p),
})
this is my model.py:
class Page(models.Model):
pub_date = models.DateTimeField('date published')
title = models.CharField(max_length=255)
keywords = models.CharField(max_length=255)
description = models.CharField(max_length=255)
content = models.TextField()
def __unicode__(self):
return self.title
this is my pageform.html
<form action="/detail/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
this is my urls.py
urlpatterns = patterns('',
url(r'^page/(?P<id>\d+)/$', 'detail', name='detail'),
)
You need to create form for get request as well.
Update your view as
def detail(request, page_id):
if request.method == 'POST':
....
else:
p = Page.objects.get(pk=page_id)
form = PageForm(instance=p)
....
Also, do not create new form when form is not valid as it will not show any errors.
Thank you Cathy and Rohan! This worked for me:
views.py
def detail(request, page_id):
p = get_object_or_404(Page, pk=page_id)
if request.method == 'POST':
form = PageForm(request.POST or None, instance=p)
if form.is_valid():
form.save()
return HttpResponseRedirect('/thanks/')
else:
form = PageForm(instance=p)
return render_to_response('pages/detail.html', {'page': p, 'form': form}, context_instance=RequestContext(request))
urls.py:
urlpatterns = patterns('',
url(r'^page/(?P<page_id>\d+)/$', 'pages.views.detail'),
)