I am trying to save a file and some other details in django using forms.
And I only want it to save a CharField and a FileField but not the country field.
For country field I want it to take its value through a post request.
But the form isn't saving. The errors says "data didn't validate".
Also this method works fine if I don't use a FileField.
models.py
class Simple(models.Model):
name = models.CharField(max_length=100)
city = models.FileField(upload_to='marksheet')
country = models.CharField(max_length=100)
forms.py
class SimpForm(forms.ModelForm):
class Meta:
model = Simple
fields = ['name','city']
A snippet from upload.html
<form action="upload" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<label>Test input</label>
<input type="text" name="country">
{{form.name}}
{{form.city}}
<button type="submit">Submit</button>
</form>
views.py
def upload(request):
if request.method == 'POST':
a = request.POST.get('country')
form = SimpForm(request.POST,request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.country = a
post.save()
return HttpResponse('saved')
else:
return HttpResponse('ERROR SAVING')
else:
form = SimpForm()
return render(request,'upload.html',{'form':form})
You are not passing request.FILES in your form. You should pass it like this:
form = SimpForm(request.POST, request.FILES)
More information on file uploads can be found in documentation.
Related
I am trying to create a django website similar to that of Udemy or Coursera. I am trying to implement a feature that lets users add courses.
Here is my view for adding the course:
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
form = CreateCourseForm(request.POST)
if form.is_valid():
new_course = form.save(commit=False)
new_course.instructor = request.user
new_course.save()
return redirect('home')
return render(request, 'courses/create.html',{'form':form})
Here is my form:
class CreateCourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ('name','video','description','category',)
My course model:
class Course(models.Model):
name = models.CharField(max_length=200)
instructor = models.ForeignKey(User, on_delete=models.CASCADE)
video = models.FileField(upload_to='videos/')
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
And finally my web form:
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<h1>Create a Course</h1>
<hr>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-sm btn-outline-primary" type="submit">Create Course</button>
</form>
{% endblock %}
The issue i am facing is that when i try to create a course, the video field shows an error "This field is required" even when I have uploaded a video.
I tried researching a bit and found out that adding enctype="multipart/form-data" to the form was required, but even adding that didn't solve my problem
Can anyone help me here?
You need to pass request.POST and request.FILES to the form, so:
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
# add request.FILES ↓
form = CreateCourseForm(request.POST, request.FILES)
# …
return render(request, 'courses/create.html',{'form':form})
If you only pass request.POST, you are only passing the items of the form that are not file uploads. This then will indeed add an error to the form fields of these files, saying that the form requires data for these fields.
as willem told you have to request file in you form like this
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
form = CreateCourseForm(request.POST, request.FILES)
if form.is_valid():
new_course = form.save(commit=False)
new_course.instructor = request.user
new_course.save()
return redirect('home')
return render(request, 'courses/create.html',{'form':form})
but i think you have to know one more you don't have to pass / in while telling django where to upload that file
class Course(models.Model):
name = models.CharField(max_length=200)
instructor = models.ForeignKey(User, on_delete=models.CASCADE)
video = models.FileField(upload_to='videos')
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
offcourse willem answer will solve your problem but you will see in your media folder a new folder name videos/ got created where you file is storing instead of your actual folder becuase you are telling django to upload that file in folder name vedios/ and if django doesn't find that folder it will create that folder with that name and then stat uploading it
I currently have a model that is called services, shown here...
class Services(models.Model):
service_sku = models.AutoField(primary_key=True)
name = models.CharField(max_length=200, null=True)
price = models.FloatField(null=True)
forms.py
class ServiceForm(forms.ModelForm):
class Meta:
model = Services
fields = '__all__'
Based on the code shown above, I have two views, one will create instances of the model above and the other view will update the instance, as follows
def NewServices(request):
form = ServiceForm()
if request.method == 'POST':
form = ServiceForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('/')
return render(request, 'accounts/new_services.html', {'form': form})
def EditServices(request,pk):
service = Services.objects.get(service_sku=pk)
form = ServiceForm(instance=service)
if request.method == 'POST':
form = ServiceForm(request.POST, instance=service)
if form.is_valid():
form.save()
return redirect('/')
context = {'form':form}
return render(request, 'accounts/edit_services.html',context)
Template as follows
<div class="row">
<div class="col-md-7">
<div class="card card-body">
<form action="" method="POST">
{% csrf_token %}
{{form}}
<hr>
<input type="submit" name="Update">
</form>
</div>
</div>
</div>
Is it possible to show a readyonly of the service_sku within in my template form, when a instance needs to be updated and when a service_sku readonly is shown to be autogenerated in a template when creating a instance?
Yes. You can include the field in a form, so likely you should make a separate one to update:
class ServiceEditForm(forms.ModelForm):
service_sku = forms.IntegerField(disabled=True)
class Meta:
model = Services
fields = ('service_sku', 'name', 'price')
In the form you then simply use that new form:
def EditServices(request,pk):
service = Services.objects.get(service_sku=pk)
if request.method == 'POST':
form = ServiceEditForm(request.POST, request.FILES, instance=service)
if form.is_valid():
form.save()
return redirect('/')
else:
form = ServiceEditForm(instance=service)
context = {'form':form}
return render(request, 'accounts/edit_services.html', context)
Using disabled=True [Django-doc] does not only make sure the field is disabled at the client side, but it will also prevent a person to make a malicious POST request.
I'm trying to get a simple form working. Oddly, other forms I wrote in this app are working fine, but this one wont show the fields. Can anyone tell me what I'm missing? Here are the files
views.py:
def newnote(request, record_id):
if request.method == 'POST':
form = NoteForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/tracker/all/')
else:
form = NoteForm()
return render(request, 'tracker/noteform.html', {'form': form})
models.py
class Note(models.Model):
record = models.ForeignKey(Record, on_delete=models.CASCADE)
note_text = models.CharField('Notes', max_length=2000)
note_date = models.DateField('Date Entered')
forms.py
class NoteForm(forms.Form):
class Meta:
model = Note
fields = ['note_text',
'note_date'
]
template (noteform.html)
<form action="/tracker/newnote/" method="post">
<div id="fields">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</div>
</form>
One other note, I have commented out the div id called "fields", to rule out CSS as the issue.
Your form is based on form.Form, which doesn't know anything about models, doesn't expect a Meta class, and expects all its fields to be declared manually - since you have not declared any fields, nothing will show on the template.
It should inherit forms.ModelForm instead.
Looking to upload 2 files into a Django Form using HTML5 (since it supports multi-file upload). The problem I'm facing is it's targets the 1st one for uploading. It knows there are 2 files, because when it saves, it saves twice (as per the for loop below). I thought to use the dictionary to loop over the names, but I receive an error that says this keyword can't be an expression. Maybe this is something simple, but if you need more, I can provide. Just a note, I did not use the forms.py for the file upload, but instead just the regular HTML <input tag. Thanks.
#page.html
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form_a.as_p }}
<input type="file" name="img" multiple>
<input type="submit" value="Submit" />
</form>
#models.py
def contact(request):
if request.method == 'POST':
form_a = RequestForm(request.POST, request.FILES)
if form_a.is_valid():
#assign form data to variables
saved_first_name = form_a.cleaned_data['First_Name']
saved_last_name = form_a.cleaned_data['Last_Name']
saved_department = form_a.cleaned_data['Department']
saved_attachments = request.FILES.getlist('img')
#create a dictionary representing the two Attachment Fields
tel = {'keyword1': 'Attachment_2', 'keyword1': 'Attachment_1'}
for a_file in saved_attachments:
#for every attachment that was uploaded, add each one to an Attachment Field
instance = Model(
Attachment_1=a_file,
Attachment_2=a_file
)
instance.save()
all_together_now = Model(First_Name=saved_first_name, Last_Name=saved_last_name,
Department=saved_department, Attachment_1=???, Attachment_2=???)
#save the entire form
all_together_now.save()
else:
#just return an empty form
form_a = RequestForm()
return render(request, 'vendor_db/contact.html', {'form_a': form_a})
Here is a way that worked for me. I loop each occurrence of an InMemoryUploadedFile in request.FILES and re-assign it back onto request.FILES, then save each one by one.
forms.py
class PhotosForm(forms.ModelForm):
file = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
class Meta:
model = Photos
fields = ['file']
views.py
def photos(request):
photos = Photos.objects.all()
if request.method == 'GET':
form = PhotosForm(None)
elif request.method == 'POST':
for _file in request.FILES.getlist('file'):
request.FILES['file'] = _file
form = PhotosForm(request.POST, request.FILES)
if form.is_valid():
_new = form.save(commit=False)
_new.save()
form.save_m2m()
context = {'form': form, 'photos': photos}
return render(request, 'app/photos.html', context)
I am working on an app that has a section with with a file upload form for .txt fiels. I would like for the current user that is uploading the file to be added along with the file and the file name. Currently, I can do this successfully in the admin section but I just cant get it to save via the form itself. Any Ideas?
Here are the models:
class UploadedTextFile(models.Model):
file = models.FileField(upload_to="textfiles")
filename = models.CharField(max_length = 50)
username = models.ForeignKey(User, blank=True, null=True)
class UploadedTextFileForm(ModelForm):
class Meta:
model = UploadedTextFile
fields = ['file', 'filename']
Here is my view:
def inputtest(request):
#response for file being submited
if request.method == "POST":
form = UploadedTextFileForm(request.POST)
if form.is_valid():
new_form = form.save(commit=False)
new_form.username = request.user
new_form.save()
return render(request, 'about.html')
inputtest = UploadedTextFileForm()
return render(request, 'failed.html', {'inputtest': inputtest})
else:
inputtest = UploadedTextFileForm()
return render(request, 'inputtest.html', {'inputtest': inputtest})
Here is my html:
{% extends 'base.html' %}
{% block content %}
<form method="post">{% csrf_token %}
{{ inputtest.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock content %}
Doing it in the view (as you've shown) is the right way to do this. Most likely you're having problems because you've left username as a field on the form, and because the FK model field doesn't have blank=True set the form requires the field to be provided. You should explicitly declare just the subset fields that you want to accept user input for in the form's Meta class.
class UploadedTextFileForm(ModelForm):
class Meta:
model = UploadedTextFile
fields = ['file', 'filename']
I am not sure why you're rendering a different template when the form is not valid, but no matter what you're not providing the form object in the context. This means that you'll never see any errors the form detects, which is probably what's happening with this code - you're not seeing the error that username is not provided.