I am trying to get my app to validate the image upload field in my app's template and save any new image that is uploaded. Everything works fine on the admin side of things but when I make an image change on the front, it doesn't get saved.
Here is my model
def imageupload(instance, filename):
return os.path.join('static/petition-photos/', filename)
# Create your models here.
class Petition(models.Model):
title = models.CharField(max_length= 90, default="Enter petition title here")
created_on = models.DateTimeField(auto_now_add=True)
image = models.ImageField(null=False, upload_to=imageupload)
video = models.CharField(max_length=600, default="Enter an external video link")
petition = models.TextField(null=False, default="Type your petition here")
created_by = models.ForeignKey(User)
Here is my view class:
class NewPetitionView(generic.edit.CreateView):
model = Petition
template_name = 'petition/petition_form.html'
fields= ['title','petition', 'image', 'video']
success_url = '/dashboard/'
def form_valid(self, form):
form.instance.created_by = self.request.user
return super(NewPetitionView, self).form_valid(form)
And this is my form template:
{% include 'layout/header.html' %}
{% block content %}
<form action="" method="post">
{{form.as_p}}
<button type="submit">Submit</button>
{% csrf_token %}
</form>
{% endblock %}
When I upload an image to a new post or try to edit the image field of one of the posted items, I get a "This field is required" notification.
What am I doing wrong?
You need in your <form> add enctype="multipart/form-data" in order to let file upload work.
<form action="" method="post" enctype="multipart/form-data">
django doc.
Related
Hi so I am new to Django and one of the things I'm trying to do is make a simple gallery application. Somehow I can't add images through the server via the forms if I use a Model Form although I can do it using a plain form. I've tried a lot of the stuff in here and also tried some Youtube stuff but it didn't still work.
Here is my models.py
from django.db import models
from django.urls import reverse
from django.core.validators import validate_image_file_extension
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location='/media')
class FavoriteImages(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(favorite=True)
# Create your models here.
class Photo(models.Model):
name = models.CharField(max_length=120, null=True)
photo = models.ImageField(storage=fs, upload_to='media/', validators=[validate_image_file_extension])
date_uploaded = models.DateTimeField(auto_now=True)
favorite = models.BooleanField(default=False, blank=False)
slug = models.SlugField(null=True, blank=True)
gallery = models.Manager()
gallery_favorites = FavoriteImages()
class Meta:
ordering = ['-date_uploaded']
My Views.py
from PIL import Image
def image_new(request, *args, **kwargs):
Image.init()
form = PhotoForm(data=request.POST, files=request.FILES)
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
form.save()
redirect('../all')
context = {
'form': form
}
return render(request, "form.html", context)
My forms.py
class PhotoForm(forms.ModelForm):
name = forms.CharField(label='',widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Title'}))
photo = forms.ImageField(widget=forms.FileInput(attrs={'class':'form-control'}))
favorite = forms.BooleanField(label='Mark as Favorite',widget=forms.CheckboxInput(attrs={'class':'form-check-input'}))
class Meta:
model = Photo
fields = ['name',
'photo',
'favorite']
my .html
{% extends "base.html" %}
{% block content %}
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post">
This form is a multipart.
{% else %}
<form method="post">
{% endif %}
{% csrf_token %}
{% if form.media %}
{{ form.media }}
{% endif %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Save"/>
</form>
{% endblock %}
I've placed this in the settings:
MEDIA_URL = 'media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Something I noticed:
The media folder (root) remains empty, but Model.photo has an url. (not null)
How do I modify my form so that the image gets posted?
EDIT: I fixed it by changing the widget of the ImageField. I don't know why it works now, but it does. Thanks for all the help
Why all those {% if %} in the template? I think it's unecessary. I would write it as follows,
{% extends "base.html" %}
{% block content %}
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary" value="Save"></button>
</form>
{% endblock %}
Your form can also be simplified as such,
class PhotoForm(forms.ModelForm):
name = forms.CharField(label='',widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Title'}))
photo = forms.ImageField()
favorite = forms.BooleanField(label='Mark as Favorite',widget=forms.CheckboxInput(attrs={'class':'form-check-input'}))
'''
You don't need the Meta class when inheriting from forms.ModelForm. I think the widgets aren't necessary, unless you need to style with CSS specifics.
Your view can also be simplified quite a bit. You don't need PIL Image unless you are modifying your image.
I would write like this,
def image_new(request, *args, **kwargs):
form = PhotoForm()
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
form.save()
redirect('../all')
context = {
'form': form
}
return render(request, "form.html", context)
Django will take care of saving the image to your media folder and assigning it to the ImageField in the model.
I'm working on a new project and I'm having difficulties adding new objects from the users page. It can be added from admin dashboard.
This is the model:
class Product(models.Model):
title = models.CharField(max_length=150)
price = models.IntegerField()
image = models.ImageField(upload_to='products')
description = models.TextField(max_length=500)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('product-details', kwargs={'pk': self.pk})
I have this view:
class ProductCreateView(LoginRequiredMixin, CreateView):
model = Product
fields = ['title', 'image', 'description', 'price']
def form_valid(self, form):
form.instance.owner = self.request.user
#form.save() # tried this too and it didn't work
return super().form_valid(form)
product_form.html:
{% extends "index/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Product</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Save</button>
</div>
</form>
</div>
{% endblock content%}
I tried a couple of times and I didn't work. So I searched for solutions and tried the following:
instance = form.save(commit=False)
instance.owner = self.request.user
instance.save()
return super().form_valid(instance)
and this
self.object.owner = self.request.user
self.object = form.save()
return super(ProductCreateView, self).form_valid(form)
within the form_valid(). Neither of them worked. So I can open the form and fill the fields. When I send it, the object is not saved but it doesn't give any error. It just reloads the form.
Ok, so after nearly one week of trying to fix the issue and not much help, I found a solution! form_valid() works fine the way it is, but the problem is in the form. Adding enctype fixes the problem:
<form method="POST" enctype="multipart/form-data">
The explanation I found for this is that without enctype the image data is not being passed correctly to the database. So it looks like the form is fine, but on the background it is not saving the image.
Class below is used to create a form in Django and get neccesary information. Problem is, it offers only one file to upload. I need to upload multiple files. I use Crispy forms.
My simplified view.py looks like:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'file_1']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
HTML code:
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Fill form</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Submit</button>
</div>
</form>
</div>
{% endblock content %}
When I inspect page, object looks like:
<input type="file" name="file_1" class="clearablefileinput form-control-file" id="id_file_1">
I want it to contain multiple atribute. How can I achive that? I can't get it to work using documentation (https://docs.djangoproject.com/en/3.2/topics/http/file-uploads/).
I have tried:
widgets = {'file_1': form.ClearableFileInput(attrs={'multiple': True})}
form.instance.file_1 = form.FileField(widget=form.ClearableFileInput(attrs={'multiple':True}))
form.instance.file_1 = form.FileField(widget=form.FileInput(attrs={'multiple': True}))
My models.py
file_1 = models.FileField(blank=True, upload_to='PN_files/%Y/%m/%d/', verbose_name="File 1", validators=[validate_file_size], help_text="Allowed size is 50MB")
I can't find an example how to implement multiple files upload which could be implemented in my class.
UPDATE! (thanks to 'amadou sow')
I've updated my class to:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'file_1']
def form_valid(self, form):
form.instance.author = self.request.user
obj = form.save(commit=False)
if self.request.FILES:
for f in self.request.FILES.getlist('file_1'):
obj = self.model.objects.create(file=f)
return super().form_valid(form)
And I've added script to my HTML page to add atribute of MULTIPLE:
<script>
$(document).ready(function(){
$('#id_file_1').attr("multiple","true");
})
</script>
Now I get an option to select multiple files and upload them, but when I do that, only 1 file is stored in my media folder.
To answer your question i will try to create a foreign key to the post and i will use function views
app/models.py:
class Post(models.Model):
#add your all other fields here execept (files)
class File(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE)
file = models.FileField(blank=True, upload_to='PN_files/%Y/%m/%d/', verbose_name="Files", validators=[validate_file_size], help_text="Allowed size is 50MB")
app/forms.py:
from .models import Post,File
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [#the field that you want to rendered]
class PostFileForm(PostForm): #extending form
file = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
class Meta(PostForm.Meta):
fields = PostForm.Meta.fields + ['file',]
app/views.py:
from .forms import PostFileForm
from .models import File
def postcreate(request):
if request.method == 'POST':
form=PostFileForm(request.POST or None,request.FILES or None)
files = request.FILES.getlist('file')
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
#add everything you want to add here
post.save()
if files:#check if user has uploaded some files
for f in files:
File.objects.create(post=post,file=f)
return redirect('the-name-of-the-view')
else:
form = PostFileForm()
return render(request,'the-template-you-want-to-rendered-',{'form':form})
with this you can upload many files you want.and if you don't want the file to be required you can add "required=False" inside the PostFileForm.
Hi Guys
I started coding in Django and i just wanted to make an 9gag-Clone.
I followed some Tutorials and acctualy made a Blog. But when i "upload" Images it allways take the default value.
So here is my Html:
{% extends "posts/post_base.html" %}
{% load bootstrap3 %}
{% block post_content %}
<h3 class="title">Poste Some Memes here</h3>
<form action="{% url 'posts:create' %}" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" value="posten" class="btn btn-primary btn-large">
</form>
{% endblock %}
Here is my Views.py:
class CreatePost(LoginRequiredMixin, SelectRelatedMixin, generic.CreateView):
fields = ('title','picture','group')
model = models.Post
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
and at least my models.py:
class Post(models.Model):
user = models.ForeignKey(User,related_name='posts')
created_at = models.DateTimeField(auto_now=True)
title = models.CharField(max_length=30,default='Titel')
picture= models.ImageField(upload_to=settings.MEDIA_ROOT, default='/static/img/default.png')
title_html = models.CharField(max_length=30,default='Titel', editable = False)
group = models.ForeignKey(Group,related_name='posts',null=True,blank=True)
def __str__(self):
return self.title
def save(self,*args,**kwargs):
self.title_html =misaka.html(self.title)
super().save(*args,**kwargs)
def get_absolute_url(self):
return reverse('posts:single',kwargs={'username':self.user.username,'pk':self.pk})
class Meta:
ordering = ['-created_at']
#unique_together = ['user','title','bild']
urls.py and other htmlfiles work correctly.
everything was makemigrated and migrated
I just need to know why it dont save the Images, or dont upload it.
Just replace
<form action="{% url 'posts:create' %}" method="POST">
with
<form action="{% url 'posts:create' %}" method="POST" enctype="multipart/form-data>
Uploaded image are at self.request.FILES
self.object.picture = self.request.FILES or self.request.FILES.get('key')
self.object.save()
You can POST data in self.request.POST and file in self.request.FILES
Both answers where right so couldn't pick them bouth.
So here is the solution:
Added
enctype="multipart/form-data"
to my HTML-Form
AND
added
self.bild = self.request.FILES['bild']
to my CreatePost
Ive tried to submit a model class using a view function but the form didn't submitted.
This The model class inside models.py file :
class Campaign(models.Model):
username = models.OneToOneField(User, unique=True)
title = models.CharField(max_length=200)
message = models.TextField()
link = models.CharField(max_length=200)
added= models.DateTimeField(auto_now_add=True)
and this its form inside forms.py :
class CampaignForm(forms.ModelForm):
class Meta:
model = Campaign
fields=('title','message')
Ive tried to submit this form using this function at views.py :
def Campaign(request):
form = CampaignForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
submit= Campaign(username=request.user)
submit.save()
context={
'form':form,
}
return render(request, 'campaigns.html',context)
And this is the html file :
{% extends "base.html" %}
{% block content %}
<form action="" method="POST" class="">
{% csrf_token %}
{{form.as_p}}
<input type="submit">
</form>
{% endblock content %}
You have to do action="#" in your form html because some browsers can not accept empty action values.
Also you have a mistake in this part of code:
submit= Campaign(username=request.user)
submit.save()
You have to get the form values into your object, you can do it this way:
submit = form.save(commit=False)
submit.username = request.user
submit.save()
Some browsers will fail to post if the action attribute is empty, try with
<form action="." method="POST">
Notice the dot ".", since you are using the same view to display the form and process it the dot it's ok.