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 })
Related
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 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
I currently have 2 models in my App, one for uploaded files and another one for comments. I've figured out how to implement the forms on the same page and it seems to be working but for some reason, the submitted comments aren't being shown and I have no idea on how to accomplish that. Should I provide the comments in a return or seeing as that there is a ForeignKey in the Comment model this is not neccesary and the comments can be accessed through each file?
Each file will have it's own comments
# Model for all uploaded files
class Uploaded(models.Model):
objects: models.Manager()
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="users")
time_uploaded = models.DateTimeField(auto_now_add=True)
description = models.CharField(max_length=100)
file = models.FileField(upload_to=MEDIA_ROOT)
admintags = TaggableManager(blank=True, through=AdminTagsAll, verbose_name="Admin Tags")
usertags = TaggableManager(through=UserTagsAll, verbose_name="User Tags")
additional_description = models.CharField(max_length=50, blank=True)
def __str__(self):
return f"{self.description} {self.file}"
# Model for comments
class Comment(models.Model):
objects: models.Manager()
file = models.ForeignKey('Uploaded', on_delete=models.CASCADE, related_name='comments')
text = models.TextField()
def __str__(self):
return self.text
HTML template:
{% for comment in comments %}
<div style="border:1px solid silver; border-radius:15px; padding:15px; margin:10px 0px;">
<p class="mb-auto" style="font-size:15px;">{{ comment.text }}</p>
</div>
{% empty %}
<div style="border:1px solid silver; border-radius:15px; padding:15px; margin:10px 0px;">
<p class="mb-auto" style="font-size:15px;">No Comments</p>
</div>
{% endfor %}
Views.py
def index(request):
url_parameter = request.GET.get("q")
user = request.user
files = Uploaded.objects.filter(user=user)
userTags = UserTags.objects.all()
form = FileUploadForm
paginator = Paginator(files, 6)
page = request.GET.get('page', 1)
try:
files_paginated = paginator.page(page)
except PageNotAnInteger:
files_paginated = paginator.page(1)
except EmptyPage:
files_paginated = paginator.page(paginator.num_pages)
if url_parameter:
filesSearch = Uploaded.objects.filter(user=user, name__icontains=url_parameter)
else:
filesSearch = Uploaded.objects.filter(user=user)
context = {'form': form, 'page': page, 'files': files_paginated, 'userTags': userTags, 'comments':comments}
def add_comment_to_file(request, pk):
file = get_object_or_404(Uploaded, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.file = file
comment.save()
return HttpResponseRedirect(reverse('index'))
else:
form = CommentForm()
return render(request, 'index.html', {'form': form})
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.
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))