User won't auto save in View, but does in Admin - python

Building a form to let users submit posts as well as an image. My model has a text field, image, publish date, and author field. Publish Date and Author auto save without any user input. It works on the admin side with admin.py. However, when displaying this through a view, it is not working. Why?
#models.py
class Question(models.Model):
text = models.CharField(max_length = 500)
image = models.ImageField(upload_to = 'movie_poster')
pub_date = models.DateTimeField(auto_now_add = True)
author = models.ForeignKey(User)
def __unicode__(self):
return self.title
class QuestionForm(ModelForm):
class Meta:
model = Question
exclude = ('author', 'pub_date')
#views.py
def add_question(request):
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
if request.method == "POST":
form = QuestionForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect("/home/")
else:
form = QuestionForm
return render_to_response("qanda/add_question.html", {'form': form}, context_instance = RequestContext(request))
#add_question.html
{% block content %}
<div class="loginform">
<h1> Add Movie:</h1>
<form enctype = "multipart/form-data" action = "" method = "post">{% csrf_token %}
{{ form.as_p }}
<input type = "submit" value = "Add" />
<input type = "hidden" name = "next" value = "{{ next|escape }}" />
</form>
{% endblock %}

save_model is not magic that works everywhere just by being there. We'll need to customize the form:
class QuestionForm(ModelForm):
def save(self, user=None, force_insert=False, force_update=False, commit=True):
q = super(QuestionForm, self).save(commit=False)
q.author = user
if commit:
q.save()
return q
class Meta:
model = Question
exclude = ('author', 'pub_date')
def add_question(request):
if request.method == "POST":
form = QuestionForm(request.POST, request.FILES)
if form.is_valid():
form.save(user=request.user)
return HttpResponseRedirect("/home/")
else:
form = QuestionForm()
return render_to_response("qanda/add_question.html", {'form': form}, context_instance=RequestContext(request))

Related

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>

Reason for form not showing although I added it in the template

I have made a new Comment Model in my Django Project but the form is not showing in the browser although I added the form in the template.
Here is the models.py
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
body = models.TextField(max_length=300)
def __str__(self):
return str(self.pk)
Here is the views:
def comment_create(request, self):
post = get_object_or_404(Post, slug=self.kwargs['slug'])
user = User.objects.get(user=request.user)
c_form = CommentModelForm()
context = {
'post': post,
'user': user,
'c_form': c_form,
}
return context
Here is the forms.py
class CommentModelForm(forms.ModelForm):
body = forms.CharField(label='',
widget=forms.TextInput(attrs={'placeholder': 'Add a comment...'}))
class Meta:
model = Comment
fields = ('body',)
Here is the urls.py
path('blogs/comment', comment_create, name='comment-post'),
Here is the template:
<form action="" method="POST"class='ui fluid form'>
{% csrf_token %}
<input type="hidden" name="post_id" value={{post.id}}>
{{ c_form }}
<button type="submit" name="submit_c_form" class="">Send</button>
</form>
First, you have to get the type of request, I added a if/else for GET and POST requests. Added form.is_valid check.
In your function you are trying to get a kwarg form the url but you don't have a kwarg in your path.
path('blogs/<slug:slug>/comment', comment_create, name='comment-post'),
views.py
def comment_create(request, self):
post = get_object_or_404(Post, slug=self.kwargs['slug'])
if request.method == 'POST': # If user submitted form
c_form = CommentModelForm(request.POST) # Get form response
if c_form.is_valid(): # Chekc if form is valid
c_form.user = User.objects.get(user=request.user) # Get user and add it to form
c_form.post = post # Add post to form
c_form.save() # Save form
else:
c_form = CommentModelForm() # Pass form
context = {
'post': post,
'c_form': c_form,
}
return render(request, 'app/comment.html', context) # change template
forms.py
class Meta:
model = Comment
fields = ['body'] # When I set tuples it normally gives me an error

Django formset creates multiple inputs for multiple image upload

I am trying to create a simple post sharing form like this one.
I'm using formset for image upload. But this gives me multiple input as you can see. Also each input can choose single image. But I'm trying to upload multiple image with single input.
views.py
def share(request):
ImageFormSet = modelformset_factory(Images,
form=ImageForm, extra=3)
# 'extra' means the number of photos that you can upload ^
if request.method == 'POST':
postForm = PostForm(request.POST)
formset = ImageFormSet(request.POST, request.FILES,
queryset=Images.objects.none())
if postForm.is_valid() and formset.is_valid():
post = postForm.save(commit=False)
post.author = request.user
post.save()
for form in formset.cleaned_data:
# this helps to not crash if the user
# do not upload all the photos
if form:
image = form['image']
photo = Images(post=post, image=image)
photo.save()
return redirect("index")
else:
print(postForm.errors, formset.errors)
else:
postForm = PostForm()
formset = ImageFormSet(queryset=Images.objects.none())
return render(request, "share.html", {"postForm": postForm, 'formset': formset})
share.html
<form method="POST" id="post-form" class="post-form js-post-form" enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
</form>
if you need, forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ["title", "content"]
def __init__(self, *args, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
self.fields['title'].widget.attrs.update({'class': 'input'})
self.fields['content'].widget.attrs.update({'class': 'textarea'})
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Images
fields = ('image', )
def __init__(self, *args, **kwargs):
self.fields['image'].widget.attrs.update(
{'class': 'fileinput', 'multiple': True})
models.py
from django.db import models
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
class Post(models.Model):
# on_delete ile, bu kullanıcı silindiğinde bu kullanıcıya ait tüm postlar da silinecek.
author = models.ForeignKey(
"auth.User", on_delete=models.CASCADE, verbose_name="Yazar")
title = models.CharField(max_length=150, verbose_name="Başlık")
content = models.TextField(verbose_name="İçerik")
# auto_now_add = True ile veritabanına eklendiği tarihi otomatik alacak
created_date = models.DateTimeField(auto_now_add=True)
# admin panelinde Post Object 1 yazması yerine başlığı yazsın istersek...
def __str__(self):
return self.title
def get_image_filename(instance, filename):
title = instance.post.title
slug = slugify(title)
return "post_images/%s-%s" % (slug, filename)
class Images(models.Model):
post = models.ForeignKey(
Post, on_delete=models.DO_NOTHING, default=None)
image = models.ImageField(upload_to=get_image_filename,
verbose_name='Image', default="images/default_game_img.png")
If you need one filed for multiple image upload, try this:
views.py
from .forms import PostForm
from .models import Post, Images
def share(request):
form = PostForm()
if request.method == 'POST':
post = Post()
post.title = request.POST['title']
post.content = request.POST['content']
post.author = request.user
post.save()
for image in request.FILES.getlist('images'):
image_obj = Image()
image_obj.post_id = post.id
image_obj.image = image
image_obj.save()
return render(request, 'share.html', {'form': form})
forms.py
from django import forms
class PostForm(forms.Form):
title = forms.CharField(label='',
widget=forms.TextInput(attrs={
'class': 'input',
}
))
content = forms.CharField(label='',
widget=forms.Textarea(attrs={
'class': 'textarea',
}
))
images = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
share.html
<form method="POST" id="post-form" class="post-form js-post-form" enctype="multipart/form-data">
{% csrf_token %}
{% for elem in form %}
{{ elem }}
{% endfor %}
</form>
I think you are over thinking the implementation you should try lib like django-multiupload
And can find valid implementation for multiple image upload through this link
https://stackoverflow.com/a/44075555/10798048
Hope this will ease your implementation and solve your problem. Correct me if it doesn't work out for you.

Django model nullable field error makemigrations

Hello I'm building my own blog and I've been trying to add photos to my blog posts. For now, I can upload photos to the media/documents folder however I'm having problems assigning these photos to posts.
This is how my models.py looks like
class Document(models.Model):
post = models.ForeignKey('blog.Post', related_name='documents', null = True)
description = models.CharField(max_length=255, blank=True)
document = models.FileField(upload_to='documents/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.text
forms.py
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ('description', 'document')
views.py
#login_required
def model_form_upload(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.post = post
form.save()
return redirect('/')
else:
form = DocumentForm()
return render(request, 'blog/model_form_upload.html', { 'form': form })
inside post_detail.html
{% for document in post.documents.all %}
<div style="padding: 10px;">
<p> {{ document.description }} </p>
<img src=""/>
</div>
{% endfor %}
...
{% if user.is_authenticated %}
<a class="btn btn-default" href="{% url 'model_form_upload' pk=post.pk %}">Add photo</a>
{% endif %}
For now, I'm trying to print out the description and then dealing with the urls will be easier. The photos are being uploaded, they just aren't connected to the posts. When I remove the null = True I can't pass makemigrations on the command line. Can anyone help me with what's wrong here?
edit: If it helps, the comments class has the same line of code, but still works just fine:
models.py
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
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text')
Your DocumentForm has no post attribute, so it does not know how to save it.
Add to your DocumentForm all fields except post:
class DocumentForm(ModelForm):
class Meta:
model = Document
exclude = ['post']
Then, in the view, do the following:
#login_required
def model_form_upload(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.post = post
document.save()
return redirect('/')
else:
form = DocumentForm()
return render(request, 'blog/model_form_upload.html', { 'form': form })

Django model formset factory and forms

I'm trying to user Django model formset factory to render a template where a user can add images and change the images they have uploaded(very similar to what can be done in the admin). I currently can render the template and its correct fields to the template. What I cannot do is have the user preselected(want currently logged in) and when I refresh the page the image will be posted again(not sure if this is preventable). Below is my code. Thanks!
Model:
class Image(models.Model):
user = models.ForeignKey(User)
image = models.ImageField(upload_to=content_file_name, null=True, blank=True)
link = models.CharField(max_length=256, blank=True)
Form:
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Image
fields = ('image',
'link',
)
View:
#login_required
def register(request):
user_data = User.objects.get(id=request.user.id)
ImageFormSet = modelformset_factory(Image,
fields=('user', 'image', 'link'), extra=3)
if request.method == 'POST':
print '1'
formset = ImageFormSet(request.POST, request.FILES, queryset=Image.objects.all())
if formset.is_valid():
formset.user = request.user
formset.save()
return render(request, 'portal/register.html', {'formset': formset, 'user_data': user_data})
else:
print '2'
formset = ImageFormSet(queryset=Image.objects.all())
return render(request, 'portal/register.html', {'formset': formset, 'user_data': user_data})
Template:
<form id="" method="post" action=""
enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
<input type="submit" name="submit" value="Submit" />
let me explain the way you can do it.
MODELS
from django.utils.text import slugify
from django.db import models
from custom_user.models import AbstractEmailUser
# User model
class UserModel(AbstractEmailUser):
full_name = models.CharField(max_length=255)
def __str__(self):
return str(self.id)
# Function for getting images from instance of user
def get_image_filename(instance, filename):
title = instance.id
slug = slugify(title)
return "user_images/%s-%s" % (slug, filename)
# Save images with user instance
class UserImages(models.Model):
user = models.ForeignKey('UserModel', db_index=True, default=None)
image = models.ImageField(upload_to=get_image_filename, verbose_name='Image', db_index=True, blank=True, null=True)
In forms it's a just a two form, one for model User, other for UserImages model.
# Images forms
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image', required=False)
class Meta:
model = UserImages
fields = ('image',)
# User form
class UserForm(forms.ModelForm):
full_name = forms.CharField(required=True)
class Meta:
model = UserModel
fields = ('full_name','email','password',)
And in Views for post you can do something like this
# View
from models import *
from forms import *
#csrf_protect
def post_view(request):
template = 'some_template.html'
ImageFormSet = modelformset_factory(UserImages, form=ImageForm, extra=15)
if request.method == 'POST':
user_form = UserForm(request.POST, prefix='form1')
formset = ImageFormSet(request.POST, request.FILES, queryset=UserImages.objects.none(), prefix='form2')
if user_form.is_valid() and formset.is_valid():
# Save User form, and get user ID
a = user_form.save(commit=False)
a.save()
images = formset.save(commit=False)
for image in images:
image.user = a
image.save()
return HttpResponseRedirect('/success/')
else:
user_form = UserForm(prefix='form1')
formset = ImageFormSet(queryset=UserImages.objects.none(), prefix='form2')
return render(request, template, {'form_user':user_form,'formset':formset})
In template you are doing the right thing.

Categories

Resources