Django ImageField object not updating with input field - django

I tried updating a user's profile picture using a function view (ajax) but I keep getting the error below:
raise MultiValueDictKeyError(key)
django.utils.datastructures.MultiValueDictKeyError: 'emp_photo'
The same technique I am using is being used on CharField and TextField without issues.
Please see below for my codes.
views.py
#login_required(login_url='/login/')
#user_passes_test(user_type_combo)
def change_employee_photo(request, pk):
data = dict()
profile = get_object_or_404(Profile, pk=pk)
if request.method == 'POST':
form = EmployeePhotoForm(request.POST or None, request.FILES or None, instance=profile)
if form.is_valid():
newPic = profile(emp_photo=request.FILES['emp_photo'])
newPic.save()
data['is_valid'] = True
else:
form = EmployeePhotoForm(instance=profile)
data['form_is_valid'] = False
context = {
'form': form,
}
data['html_form'] = render_to_string('employees/partial_employee_photo.html',
context,
request=request
)
return JsonResponse(data)
forms.py
class EmployeePhotoForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['emp_photo',]
{% load crispy_forms_tags %}
<form action="{% url 'change_employee_photo' form.instance.pk %}" class="js-change-employee-photo-form"
enctype="multipart/form-data" id="ProfileUpdateForm" method="post" runat="server">
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title">Change Employee Photo</h5>
<button aria-label="Close" class="close" data-dismiss="modal" type="button">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group col">
<div id="emp_photo_holder">
<img alt="{{profile.first_name}}_{{profile.last_name}}" height="200px" id="emp_img"
src="/media/default.jpg/" width="200px"/>
<input id="emp_photo" name="emp_photo" type="file" value="{{profile.emp_photo}}"/>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('#emp_img').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
$("#emp_photo").change(function() {
readURL(this);
});
</script>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" type="button">Close</button>
<button class="btn btn-warning confirmation"
onclick="return confirm('Are you sure you want upload a new photo for this employee?')"
type="submit">
<i class="fa fa-upload"></i> Upload
</button>
</div>
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
I will most definitely appreciate any help to have this issue resolved.

i Guess you have forgot to mention the below lines in your settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"

You can think of using request.FILES.getlist('emp_photo')
and additionally check if emp_photo key exists by printing it in your code if it holds some value.

Related

Simple Django Form with Ajax Submission Not Updating Model

I'm trying to write a simple form using Ajax. After multiple attempts, I keep getting "POST /app/emails/ HTTP/1.1" 200 13950 in Terminal, but the model doesn't update (even when I look in the shell).
I really would like the abilityt to submit the form within the modal, and update the image behind it. However if that's not possible I can settle for an alternative.
Really stuck here, would love any advice!
emails.html
<div class="modal fade" id="addEmailModal" data-bs-backdrop="static" data-bs-keyboard="false"
tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Add New Email</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="POST" id='add-email'>
{% csrf_token %}
<fieldset class="form-group">
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close
</button>
<button class="btn btn-primary" type="submit">Add</button>
</div>
</form>
</div>
</div>
</div>
Javascript
$("#add-email").submit(function (e) {
e.preventDefault();
alert('working');
$.ajax({
type: 'POST',
url: "{% url 'add-emailname' %}",
data: {
email_name = $(this).email_name,
cadence = $(this).cadence
},
success: function (response) {
alert("Email name successfully added!")
$("#add-email").trigger('reset'); //clear the form
},
error: function (response) {
// alert the error if any error occured
alert(response["responseJSON"]["error"]);
}
})
}
views.py
def emails(request):
context = {
'emails': UserEmail.objects.all(),
'form': EmailCreateForm()
}
return render(request, 'app/emails.html', context)
def addEmailName(request):
if request.is_ajax and request.method == "POST":
form = EmailCreateForm(request.POST)
if form.is_valid():
form.save()
returned_email_name = form.cleaned_data.get('email_name')
messages.success(request, f'{returned_email_name} added as new Email!')
# send to client side.
return JsonResponse({"success": True}, status=200)
else:
# some form errors occured.
return JsonResponse({"error": form.errors}, status=400)
# some error occured
return JsonResponse({"error": ""}, status=400)
forms.py
class EmailCreateForm(ModelForm):
class Meta:
model = UserEmail
fields = ['email_name', 'cadence']
def __init__(self, *args, **kwargs):
super(EmailCreateForm, self).__init__(*args, **kwargs) # Call to ModelForm constructor
self.fields['email_name'].widget.attrs['rows'] = 1
self.fields['cadence'].widget.attrs['rows'] = 1
urls.py
urlpatterns = [
path('', views.home, name='app-home'),
path('emails/', views.emails, name='app-emails'),
path('post/ajax/addEmailName', addEmailName, name='add-emailname')]

Why isn't my page redirecting after I submit the form or refresh the page in Django?

I am working on a Django application but I am not getting the desired results. The create_job page is not rendering after I submit the the form or refresh the entire page.
This is the create_job_page view
def create_job_page(request):
current_customer = request.user.customer
if not current_customer.stripe_payment_method_id:
return redirect(reverse('customer:payment_method'))
# return render(request, 'customer/create_job.html')
# Filtering
create_job = Job.objects.filter(customer=current_customer, status=Job.CREATING_STATUS).last()
step1_form = forms.JobCreateStep1Form(instance=create_job)
if request.method == 'POST':
if request.POST.get('step') == '1': #If it's form one
step1_form = forms.JobCreateStep1Form(request.POST, request.FILES)
if step1_form.is_valid():
creating_job = step1_form.save(commit=False)
# Adding current customer to the form
creating_job.customer = current_customer
creating_job.save()
return redirect(reverse('customer:create_job'))
return render(request, 'customer/create_job.html', {
"step1_form": step1_form
})
This is the HTML code
<b>Create a Job</b>
<div class="tab-content" id="pills-Content">
<div class="tab-pane fade" id="pills-info" role="tabpanel" aria-labelledby="pills-info-tab">
<h1>Item Info</h1>
<form method="POST" enctype="multipart/form-data">
<b class="text-secondary">Item Information</b></br>
<div class="card bg-white mt-2 mb-5">
<div class="card-body">
{% csrf_token %}
{% bootstrap_form step1_form %}
</div>
</div>
<input type="hidden" name="step" value="1">
<button class="btn btn-primary" type="submit">Save & Continue</button>
</form>
</div>
In django i never use reverse, i generally just write redirect('app:url_name')

Django, uploading images throwing 403 CSRF token missing error on Ajax call

I am trying to upload multiple files using Django. The idea is that while creating a blog post the user can also add images to the post. I am using Ajax to submit the blog post, however, it seems that after adding the image form the "submit" button doesn't work and my post does not get created. I have already built two models, Images and Post. Below are my codes related to this functionality. My project name is register and images are in an app called 'home':
(If I remove the image upload functionality the post is created successfully, so the ajax is working correctly)
my image model:
my post_creat view in home app views:
#login_required
def post_create(request):
data = dict()
if request.method == 'POST':
image_form = ImageForm(request.POST, request.FILES or None)
images = request.FILES.getlist('image')
form = PostForm(request.POST)
if form.is_valid() and image_form.is_valid():
post = form.save(False)
post.author = request.user
post.save()
for i in images:
image_instance = Images(image=i,post=post)
image_instance.save()
data['form_is_valid'] = True
posts = Post.objects.all()
posts = Post.objects.order_by('-last_edited')
data['posts'] = render_to_string('home/posts/home_post.html',{'posts':posts},request=request)
else:
data['form_is_valid'] = False
else:
image_form = ImageForm
form = PostForm
context = {
'form':form,
'image_form':image_form
}
data['html_form'] = render_to_string('home/posts/post_create.html',context,request=request)
return JsonResponse(data)
my javascript code for handling the ajax request:
$(document).ready(function(){
var ShowForm = function(e){
e.stopImmediatePropagation();
var btn = $(this);
$.ajax({
url: btn.attr("data-url"),
type: 'get',
dataType:'json',
beforeSend: function(){
$('#modal-post').modal('show');
},
success: function(data){
$('#modal-post .modal-content').html(data.html_form);
}
});
return false;
};
// change form to FormData
// var form = $(this)
// processData, contentType were removed
var SaveForm = function(e){
e.stopImmediatePropagation();
var data = new FormData($('form').get(0));
$.ajax({
url: $(this).attr('data-url'),
type: $(this).attr('method'),
data: data,
processData: false,
contentType: false,
dataType: 'json',
success: function(data){
if(data.form_is_valid){
$('#post-list div').html(data.posts);
$('#modal-post').modal('hide');
} else {
$('#modal-post .modal-content').html(data.html_form)
}
}
})
return false;
}
//create
$('.create-post-btn').click(ShowForm);
$('#modal-post').on("submit",".post-create-form",SaveForm)
//update
$('#post-list').on("click",".show-form-update",ShowForm);
$('#modal-post').on("submit",".update-form",SaveForm)
//delete
$('#post-list').on("click",".show-form-delete",ShowForm);
$('#modal-post').on("submit",".delete-form",SaveForm)
});
My post_create.html file:
<form method="POST" data-url="{% url 'home:post-create' %}" class="post-create-form" enctype="multipart/form-data">
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title" >Create a Post</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" style="overflow-y: auto;">
{{ form|crispy }}
<button type="button" id="show-image-upload-inputs" class="btn btn-sm mr-auto btn-primary pb-1">
<span><i class="fas fa-camera"></i></span>
</button>
<div id="image-upload-div" class="mt-1" style="display: none;">
<hr class="my-2">
<p class="mx-2 text-muted small">Add up to four images to your post</p>
<div class="d-flex flex-row justify-content-between px-1">
{{ image_form }}
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Post</button>
</div>
</form>
**the display hidden is just a javascript functionality to show the form when use clicks on the vamera button.
my image model:
class Images(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name='images')
image = models.FileField(upload_to=upload_to_uuid('media/post_images/'),verbose_name='Image')
date_added = models.DateTimeField(auto_now_add=True)
my project (register) settings.py:
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
my project (register) urls.py;
urlpatterns = [
#Has to be included for Forgot Password funcitonality on main page
path('', include('django.contrib.auth.urls')),
path('admin/', admin.site.urls),
path('',views.user_login,name='user_login'),
path('',include('main.urls'),name='main'),
url(r'^home/',include(('home.urls','home'), namespace='home'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT ) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
and my media folder is created at register/media
EDIT: I have changes my Ajax function and now I'm using FormData, however, I am getting a
Forbidden (CSRF token missing or incorrect.): /home/post/create/
[06/Apr/2020 01:18:53] "POST /home/post/create/ HTTP/1.1" 403 2513
Error,when I clikc on submit, however, I do have a csrf_token.How can I resolve this issue?
Your Code Post Model Should be like this
class Post(models.Model):
title=models.CharField(max_length=50)
sub_title=models.CharField(default="This is the subtitle of the post ",max_length=150)
content=models.TextField(max_length=4000)
date_posted=models.DateTimeField(default=timezone.now)
blog_image_1=models.ImageField(Blank=True,Null=True,upload_to='media')
blog_image_2=models.ImageField(Blank=True,Null=True,upload_to='media')
blog_image_3=models.ImageField(default='default.jpg',upload_to='media')
author =models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return self.title
The media directory be like this
MEDIA_ROOT=os.path.join(BASE_DIR,'media')
MEDIA_URL='/media/'
And Html Be like
<body>
<div class="container">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="'border-bottom mb-4">Post</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn-outline-info" type="submit"> Submit</button>
</div>
</form>
</div>
</body>
And Use Class Base Views They More PowerFull
class PostCreateView(LoginRequiredMixin,CreateView):
model = Post
fields = ['title','sub_title','content','blog_image_1','blog_image_2','blog_image_2']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)

Django: How to use modal as a form to update an object

I'm trying to use modal as a edit form but I don't know good way.
Currently my views.py is like this.
def edit_entry(request, entry_pk):
entry = get_object_or_404(Entry, pk=entry_pk)
if request.method != 'POST':
form = EntryForm(instance=entry, user=request.user)
else:
form = EntryForm(instance=entry, data=request.POST, user=request.user)
if form.is_valid():
form.save()
and I have a template for this view.
What I want to do is
from the page that lists a bunch of entry objects, the edit form for the object is shown when I click edit button.
{% for entry in entries %}
...
<button class="btn btn-outline-primary" data-toggle="modal" data-target="#editEntryModal">Edit</button>
<!-- modal for the object -->
Anyone who could give me tips?
You need to send back your form as html and stick it in the modal before you show it. so on click, you would first do you ajax request, and this will send back your form as html. For example,
def edit_entry(request, entry_pk):
....
entry = get_object_or_404(Entry, pk=entry_pk)
if request.method != 'POST':
form = EntryForm(instance=entry, user=request.user)
return HttpResponse(form.as_p()) # This will return the plain html of a form
....
Hope this helps!
I had a similar task and first created an ajax request from the template to load the data:
<script>
$(document).ready(function(){
$("#myBtn").click(function(){
var pk = $(this).data('pid')
$("#myModal").modal("show");
});
$("#myModal").on('show.bs.modal', function(event){
var modal = $(this)
var pk = $(this).data('pid')
$.ajax({
data: {'pk': pk},
url: "{% url 'search:load_paper' %}",
context: document.body,
error: function(response, error) {
alert(error);
}
}).done(function(response) {
modal.html(response);
});
});
});
</script>
The load_paper function looks like the following:
def load_paper(request):
pk = request.GET.get('pk')
object = get_object_or_404(Paper, pk = pk)
form = PaperForm(instance=object)
return render(request, 'edit_paper_modal.html', {
'object': object,
'pk': pk,
'form': form,
})
The data is rendered in the 'edit_paper_modal.html', which looks like this:
<div class="modal-dialog modal-lg" role="document">
<form action="{% url 'search:edit_paper' pk=object.pk %}" method="post" class="form" enctype="multipart/form-data">
{% csrf_token %}
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">Edit Paper</h4>
</div>
<div class="modal-body">
{% csrf_token %}
{{form|crispy}}
{{ form.media }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<input type="submit" class="btn btn-primary" value="Save changes" />
</div>
</div><!-- /.modal-content -->
</form>
</div>
The key thing here is that I am passing the pk of the paper back to the edit_paper function in views.py which actually saves the form.
Hope this is useful!
what you are trying to possible only with django template.
you need js or jquery or any frontend framework .
when you click edit button A edit from will appeared in current dom and you need to make ajax call to get initial data for edit from. the edit the data and submit the data vai ajax and in backend update the object. django rest framework is more suitable for this kind of task.

Django bootstrap modal after form submission

I am working with django and have a bootstrap3 'contact' form that I submit after jquery validation. This is working correctly and I now want to redirect to a bootstrap modal 'thank you' page. I'm not exactly sure how to do this in Django. In my main page, under the form I've added:
<div class="row">
<div class="col-lg-5 col-lg-offset-1 col-sm-push-6 col-sm-6"
<div class="clearfix"></div>
<h2 class="section-heading">Registration form</h2>
{% block 'form' %}
{% endblock %}
</div>
<a data-toggle="modal" href="#thanksModal">thanksModal</a>
{% include "thanks.html" %}
<div class="col-lg-5 col-sm-pull-6 col-sm-6">
<img class="img-responsive" src="../static/img/dog.png" alt="" />
</div>
</div>
The modal itself is:
<div class="modal hide" id="thanksModal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<h1>Thank you</h1>
</div>
<div class="modal-footer">
<input name="cancel" class="btn" type="submit" value="Cancel"/>
</div>
</div>
In my view I have:
def contact(request):
errors = []
if request.method == 'POST'
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '#' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', 'noreply#simplesite.com'),
['administrator#simplesite.com'], #email address where message is sent.
)
return HttpResponseRedirect('/thanks/')
return render(request, 'form.html',{'errors': errors})
def thanks(request):
return render_to_response('thanks.html')
As it is currently, I see an unclickable link that says 'thank you' on the main page and upon submission a redirection occurs to the 'contact' page. How can I get this working?
If I understand correctly, after the contact form is submitted you want a modal to appear saying thanks.
Instead of:
return HttpResponseRedirect('/thanks/')
Do:
import json
...
return HttpResponse(json.dumps({"success":True}), content_type="application/json")
And in your JS that submits the form check the response for success before reloading the HTML form:
if (typeof jqXhr.success != 'undefined') {
$('#success-modal').modal('show');
} else {
$('#my-form').html(jqXhr);
}