How to remove old form to stop double-submission - javascript

I am using AJAX to submit a form from a modal window. If the form is submitted with errors the server will send back a new form. The container holding the old form is then removed and the new form, with validation errors, is set in it's place.
The issue is that, for some reason, it submits 2 forms at the same exact time after the validation errors are fixed. It is as-if the old form does not get removed?
It does not happen when I use the onclick=functionCall() syntax but rather when I use the $('form').on('submit', e => { syntax. I am trying to use the latter as I was told the onX events are now considered bad practice.
Any help on understanding what is happening and how to fix it would be useful.
Form:
// Submit Form
$('form').on('submit', e => {
e.preventDefault();
let data = new FormData($(e.target).get(0));
const btn = $(e.target).find('button:submit')
const container = document.getElementById('formFields')
$.ajax({
url : e.target.action,
data : data,
type : 'POST',
headers : {"X-CSRFToken": token},
enctype : 'multipart/form-data',
processData : false,
contentType : false,
success : function (data) {
btn.toggleClass('btn-warning btn-secondary').prop('disabled', true)
},
error : function (data) {
container.innerHTML = loadingSpinner;
$(container).empty();
$(container).html(data['responseJSON']['table']).fadeIn('slow');
},
});
});
View:
#login_required
def load_add_memo_form(request, pk=None):
if request.method == 'POST' and request.is_ajax():
memo_form = MemoForm(
data=request.POST,
files=request.FILES,
company=request.tenant)
memo_form.instance.sender = request.user
if pk:
memo = get_object_or_404(Memo, pk=pk)
memo_form = Memo(
data=request.POST,
files=request.FILES,
company=request.tenant,
instance=memo)
if memo_form.is_valid():
if request.user.role.supervisor is None or request.user is memo_form.sender:
memo_form.save()
reason_for_notification = 'memo to view'
notifier = SendNotifications(request, reason_for_notification, memo_form)
notifier.send_notifications()
return JsonResponse({'success': True})
else:
data = {'table': render_to_string('memos/components/add_memo_form.html', context={'form': memo_form})}
return JsonResponse(data, status=400)
else:
memo_form = MemoForm(company=request.tenant)
if pk:
memo = get_object_or_404(Memo, pk=pk)
memo_form = MemoForm(company=request.tenant, instance=memo)
context = {'form': memo_form}
template = 'memos/components/add_memo_form.html'
return render(request, template, context)
And obvious the form is just templating language...

Related

How to correctly use Fetch in JavaScript and Django?

I am trying to make a METAR decoder as shown:
I am using fetch in Vanilla js and I plan to send the entered code to a Django view. From the Django view, the decoded data will be taken and displayed in the template.
views.py
def ToolsPageView(request):
if request.method == "POST":
jsonData = json.loads(request.body)
metarCode = jsonData.get('Metar')
return JsonResponse("Success", safe=False)
return render(request, 'app/tools.html')
urls.py
...
path("tools", views.ToolsPageView, name="tools")
tools.html
<div class="metar-code-decode">
<form method="POST" action="{% url 'tools' %}" id="metar-form">
{% csrf_token %}
<input type="text" placeholder="Enter METAR: " id="metar-value"> <br>
<input type="submit" id="metar-button">
</form>
</div>
tool.js
function getDecodedMetar() {
let formButton = document.querySelector("#metar-button");
formButton.onclick = function (e) {
let metarCode = document.querySelector("#metar-value").value;
sendMetar(metarCode);
//e.preventDefault();
//getMetar(metarCode);
};
}
function sendMetar(metarCode) {
fetch('/tools', {
method: "POST",
headers: {
"X-CSRFToken": getCookie("csrftoken"),
},
body: JSON.stringify({
Metar: metarCode,
}),
});
}
I have used the same code for POST using fetch where I had to let user update his/her profile. And that worked. But, this does not work and the error keeps on changing from time to time after restarting the server. At the first try, there was no error produced and the server also showed a POST request being made. And the latest error which I am getting is "In order to allow non-dict objects to be serialized set the safe parameter to False." I get the same thing again and again even after setting safe=False within the JsonResponse(). Worth to note, request when converted to request.json() gives an error.
Am I using fetch wrongly? If yes, what is the correct way?
I'm not sure you have the flow right. The idea is that the button, when clicked, will call a function (fetch) that will send data to the view, which will decode it and send it back to the JavaScript, so that it could be displayed without reloading the entire page.
I think this might help:
let formButton = document.querySelector("#metar-button");
// When the button is clicked,
formButton.onclick = function(e) {
// do NOT send the form the usual way
e.preventDefault();
let metarCode = document.querySelector("#metar-value").value;
// Run the function that will send the code to the ToolsPageView
sendMetar(metarCode);
}
async function sendMetar(metarCode) {
const response = await fetch('/tools', {
method: "POST",
headers: {
"X-CSRFToken": getCookie("csrftoken"),
},
body: JSON.stringify({
'Metar': metarCode,
}),
})
.then(response => response.json())
.then(data => {
console.log(data);
// extract the decoded value from the data sent back from the view
// display it by targeting the element in your html that you want
// to display it
});
}
And in your view,
def ToolsPageView(request):
if request.method == "POST":
jsonData = json.loads(request.body)
metarCode = jsonData.get('Metar')
# Remove the original JsonResponse
# return JsonResponse("Success", safe=False)
# and INSTEAD,
# Send the code back to the JavaScript
# I don't THINK you need safe=False here?
return JsonResponse({'MetarCode': metarCode})
return render(request, 'app/tools.html')

Not able to figure out the way to call a function at the correct place in django website

I am working on a django website and have come across an error which I am not able to solve and Its been a couple of days but I wasnt able to solve the error. I am trying to integrate a payment gateway to my site. I have a payment button which on click is supposed to post the data to the database as well as go to the payment site. But it is storing the data but not going to payment site.
Here is the javascript for my button:
document.getElementById('payment-info').addEventListener('click', function (e) {
submitFormData()
})
function submitFormData() {
console.log('Payment Button Clicked')
var userFormData = {
'name': null,
}
var shippingInfo = {
'address': null,
}
shippingInfo.address = form.address.value
userFormData.name=form.name.value
var url = "/process_order/"
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body:JSON.stringify({'form': userFormData, 'shipping': shippingInfo }),
})
.then((response) => response.json())
.then((data) => {
console.log('Success:', data);
alert('Transaction Completed')
window.location.href = "{% url 'index' %}"
})
}
</script>
This is my views.py:
def processOrder(request):
transaction_id = datetime.datetime.now().timestamp()
data = json.loads(request.body)
if request.user.is_authenticated:
customer=request.user.customer
order, created=Order.objects.get_or_create(customer=customer, complete=False)
total=float(data['form']['total'])
order.transaction_id=transaction_id
if total == order.get_cart_total:
order.complete = True
order.save()
ShippingAddress.objects.create(
customer=customer,
order=order,
address=data['shipping']['address'],
name=data['form']['name'],
)
//Code for integration below
param_dict = {
'MID': 'DIY12386817555501617',
'ORDER_ID': str(order.id),
'TXN_AMOUNT': '4',
'CUST_ID': 'j',
'INDUSTRY_TYPE_ID': 'Retail',
'WEBSITE': 'WEBSTAGING',
'CHANNEL_ID': 'WEB',
'CALLBACK_URL':'http://127.0.0.1:8000/handlerequest/',
}
param_dict['CHECKSUMHASH'] = Checksum.generate_checksum(param_dict, MERCHANT_KEY)
return render(request, 'paytm.html', {'param_dict': param_dict})
return JsonResponse('Done',safe=False)
#csrf_exempt
def handlerequest(request):
# paytm will send you post request here
form = request.POST
response_dict = {}
for i in form.keys():
response_dict[i] = form[i]
if i == 'CHECKSUMHASH':
checksum = form[i]
verify = Checksum.verify_checksum(response_dict, MERCHANT_KEY, checksum)
if verify:
if response_dict['RESPCODE'] == '01':
print('order successful')
else:
print('order was not successful because' + response_dict['RESPMSG'])
return HttpResponse('doneee')
And this is my urls.py:
path("process_order/",views.processOrder,name="process_order"),
path("handlerequest/",views.handlerequest,name="handlerequest"),
This code is not working . After clicking the payment button I am getting this error:
Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
This error is referring to the submitformdata() in my javascript code.
I want to workout a way so that once a person clicks on the payment button and his transaction is completed then his data is submitted to the database otherwise not for which I have written the code in handlerequest.
Please help me out.

Upload image - Django rest framework

I have 2 User and EcoUser models with relation of 1 to 1 (I have reduced the fields of the tables for this example):
class User(AbstractUser):
picture_url = models.ImageField(upload_to='logos/', blank=True)
class EcoUser(models.Model):
user = models.OneToOneField(User, related_name='eco_user')
document = models.CharField(max_length=45, blank=True)
def __str__(self):
return str(self.user)
In which I use a NestedSerializer to be able to create and update the data of the two tables in a single post or put in this way I did the update since in the register I do not keep images and I have no problem with it:
This is the serializer:
class EcoUserSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = EcoUser
fields = '__all__'
def update(self, instance, validated_data):
instance.document = validated_data.get('document', instance.document)
instance.save()
user_data = validated_data.pop('user')
user = instance.user
user.picture_url = user_data.get('picture_url', user.picture_url)
user.save()
return instance
and in my viewset:
class EcoUserViewSet(viewsets.ModelViewSet):
serializer_class = EcoUserSerializer
queryset = EcoUser.objects.all()
pagination_class = None
parser_classes = (MultiPartParser,)
#transaction.atomic
def update(self, request, *args, **kwargs):
with transaction.atomic():
try:
instance = self.get_object()
instance.id = kwargs.get('pk')
serializer = EcoUserSerializer(instance=instance, data=request.data)
print(serializer)
if serializer.is_valid(raise_exception=True):
self.perform_update(serializer)
return Response({"status": True, "results": "Datos actualizados correctamente"},
status=status.HTTP_201_CREATED)
except ValidationError as err:
return Response({"status": False, "error_description": err.detail}, status=status.HTTP_400_BAD_REQUEST)
This worked correctly until I added the ImageField field and it did not update my data and I got a 400 bad request error. This I send to him of the VUEJS by axios:
const bodyFormData = new FormData();
bodyFormData.append('user.picture_url', this.params.user.picture_url.name);
bodyFormData.append('document', this.params.document);
this.axios.put(`/users/${this.params.id}/`, bodyFormData, { headers: { 'Content-Type': 'multipart/form-data' } })
.then((response) => {
this.isSending = false;
this.$snackbar.open(response.data.results);
});
It's okay if in the apppend as field name I put user.picture_url? since it is inside the user object and then I already access the picture_url to be able to update it.
Probe of the postman and I realized the error: It was simply that the username field of the django table was mandatory:
const bodyFormData = new FormData();
bodyFormData.append('user.picture_url', this.params.user.picture_url.name);
bodyFormData.append('document', this.params.document);
bodyFormData.append('user.username', this.params.username);
and it worked wonders : D

How to get around "missing 1 required positional argument" error when doing an AJAX call

I get a TypeError: options() missing 1 required positional argument: 'context' on the following code:
def post_ad(request):
filename = request.POST.get('temp_s3_url')
boost_form = AdvertisePostForm(request.POST or None, request.FILES or None)
if boost_form.is_valid():
instance = boost_form.save(commit=False)
if Post.objects.filter(hash=instance.hash).exists():
instance.hash = random_string()
instance.ad = True
instance.save()
context = {
'hash': instance.hash,
}
return options(request, context)
context = {
'boost_form': boost_form
}
return render(request, 'advertising/post_ad.html', context)
def options(request, context):
ad = get_object_or_404(AdvertisePost, hash=context['hash'])
if request.is_ajax():
total_price = request.POST.get('total_price')
print('Total price:', total_price)
context = {
'ad': ad
}
return render(request, 'advertising/options.html', context)
js
...
var total_price = Math.round(amount);
$.ajax({
type: 'POST',
url: "/advertise/options/",
data: {
csrfmiddlewaretoken: $("input[name='csrfmiddlewaretoken']").val(),
total_price: total_price,
},
success: function (data) {
console.log('Success!');
console.log(amount);
}
});
}
when I remove context as a positional argument, or change def options(request, context): to def options(request, context="something"): - it works (there's no error and the ajax call successfully prints in my view. However I have to keep the context as it has important data. How can I get around this problem?
Managed to get around it by removing the context positional argument and simply getting the latest model created by adding ad = AdvertisePost.objects.order_by('-id')[0] to my options() view - as that was what I was passing through in my initial context.

Upload file to media folder during AJAX instead of when the form is submitted

I've just got a simple upload form:
def post(request):
if request.user.is_authenticated():
form_post = PostForm(request.POST or None, request.FILES or None)
if form_post.is_valid():
instance = form_post.save(commit=False)
instance.user = request.user
instance.save()
return HttpResponseRedirect('/home/')
else:
form_post = PostForm()
context = {
'form_post': form_post,
}
return render(request, 'post/post.html', context)
else:
return HttpResponseRedirect("/accounts/signup/")
which derives from here:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [
'title',
'image',
'user'
]
and here's the models:
class Post(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
title = models.TextField(max_length=95)
image = models.FileField(null=True, blank=True)
When a user adds an image to the form, it fires this JS function:
$('input#id_image').on('change', function(e) {...}
which gives a preview of the image. This is the point where I want the image to be uploaded to my media folder directory (I'm using S3 storage). By default, the image is uploaded when the user submits the form, but I want it to be uploaded as soon as $('input#id_image').on('change' is triggered.
How would I go about this?
Just send your form as ajax request in your onChange handler:
$('input#id_image').on('change', function(e) {
$.ajax({
url: "your endpoint here",
type: "POST",
data: new FormData($(form#yourFormID)[0])
}).done(function(data) {
//on success code
}).fail(function(data, err) {
//on fail code
}).always(function() {
//whatever to be done no matter if success or error
});
}

Categories

Resources