Updating ModelForm Django 1.10 - python

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

Django prepopulate form in UpdateView

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

Error: Django model form data not being saved in the database

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>

Possible to make these two django views DRY-er?

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', ...]

Adding an ImageField to already existing model in django

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 }}" />

Creating a ModelForm to update a record in django 1.4

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'),
)

Categories

Resources