I am using fetch on the frontend to send data to my flask backend in order to make a movie seat booking. The whole process works fine until the client awaits the response, which is "undefined" . So , basically the database saves the data , the only problem is the response which is sent to the client. I used jsonify which usually works fine. Can anybody tell me what I am missing? Thanks in advance.
Here is the JS code :
function sendReservationToServer() {
const selectedSeats = sessionStorage.getItem('selectedSeats')
const reservation = { userId, selectedSeats, showTimeId, movieHallId }
fetch('/bookSeats', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(reservation)
}).then(response => {
response.json()
}).then(data => {
theatreHall.innerHTML = `${data} <br> <a href='/home'>Back to main menu</a>`
console.log(`${data}`)
}).catch(err => infoMsg.textContent = err)
sessionStorage.clear()
}
And this is the flask controller which handles the request:
#app.route("/bookSeats", methods=["POST"])
def book_seats():
selected_seats = request.json
user_id = selected_seats.get('userId')
seats = json.loads(selected_seats.get('selectedSeats'))
movie_hall_id = selected_seats.get('movieHallId')
seat_ids = []
showtime_id = selected_seats.get('showTimeId')
for seat in seats:
seat_ids.append(db.session.query(Seat).filter(
Seat.seat_number == seat).filter(Seat.movie_hall_id == movie_hall_id).all()[0].stid)
for seat in seat_ids:
reserved_seat = ReservedSeat(
seat_id=seat, show_time=showtime_id, user=user_id)
db.session.add(reserved_seat)
db.session.commit()
reservation = Reservation(
user=user_id, show_time=showtime_id, number_of_tickets=len(seat_ids))
db.session.add(reservation)
db.session.commit()
message = f'{seats} booked successfully'
return jsonify(message)
data is undefined because the first then does not return anything. Either make it return response.json() or move everything in the second then to the first and replace data with response.json().
Related
I have managed to write code to add a customer to my MongoDB collection from my Angular service method to my Django http function, as follows:
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
}),
withCredentials: false
}
#Injectable()
export class MongoService {
myApiBaseUrl = "http://localhost:8000/mydjangobaselink/";
constructor(private httpClient: HttpClient) { }
addCustomer(customerFormInfo: Customer): Observable<Customer> {
return this.httpClient.post<Customer>(`${this.myApiBaseUrl}`, JSON.stringify(customerData), httpOptions);
}
deleteCustomer(): Observable<Customer> {
return this.httpClient.delete<Customer>(`${this.myApiBaseUrl}`);
}
}
#csrf_exempt
#api_view(['GET', 'POST', 'DELETE'])
def handle_customer(request):
if request.method == 'POST':
try:
customer_data = JSONParser().parse(request)
customer_serializer = CustomerModelSerializer(data=customer_data)
if customer_serializer.is_valid():
customer_serializer.save()
# Write customer data to MongoDB.
collection_name.insert_one(customer_serializer.data)
response = {
'message': "Successfully uploaded a customer with id = %d" % customer_serializer.data.get('id'),
'customers': [customer_serializer.data],
'error': ""
}
return JsonResponse(response, status=status.HTTP_201_CREATED)
else:
error = {
'message': "Can not upload successfully!",
'customers': "[]",
'error': customer_serializer.errors
}
return JsonResponse(error, status=status.HTTP_400_BAD_REQUEST)
except:
exceptionError = {
'message': "Can not upload successfully!",
'customers': "[]",
'error': "Having an exception!"
}
return JsonResponse(exceptionError, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
elif request.method == 'DELETE':
try:
CustomerModel.objects.all().delete()
# Delete customer data from MongoDB.
collection_name.deleteMany({})
return HttpResponse(status=status.HTTP_204_NO_CONTENT)
except:
exceptionError = {
'message': "Can not delete successfully!",
'customers': "[]",
'error': "Having an exception!"
}
return JsonResponse(exceptionError, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
The POST method works fine and I can see the added document in my MongoDB Compass, but when I try to delete, I get:
DELETE http://localhost:8000/mydjangobaselink/ 500 (Internal Server Error)
All the posts and articles I have seen address communication issues in the browser, local host, etc... but given that my posting method works fine, I don't think that is my issue. Also, in Postman, I get Can not delete successfully!
Can anyone see what might be wrong that I cannot delete from database?
Try with collection_name.delete_many({})
https://pymongo.readthedocs.io/en/stable/api/pymongo/collection.html#pymongo.collection.Collection.delete_many
Edit: as pointed by #NKSM, there is the documentation copied:
delete_many(filter, collation=None, hint=None, session=None)
Delete one or more documents matching the filter:
>>> db.test.count_documents({'x': 1})
3
>>> result = db.test.delete_many({'x': 1})
>>> result.deleted_count
3
>>> db.test.count_documents({'x': 1})
0
I am trying to decrypt encrypted text and return the plain text through ajax call, instead of getting the message response I get an HTML page back as a response. I have tried returning the response as JSON but still getting the same HTML response.
function loadMessage() {
fetch("{% url 'chat:history' chatgroup.id %}")
.then( response => response.json() )
.then( data => {
for (let msg of data) {
var message=msg.message;
$.ajax({
type: 'GET',
url: '',
data: { message: message},
success: function(response){
broadcastMessage(response.message, msg.username, msg.date_created)
}
})
}
})
}
views.py
def get(request):
message = request.GET.get('message')
key = b'\xa8|Bc\xf8\xba\xac\xca\xdc/5U0\xe3\xd6f'
cipher = AES.new(key, AES.MODE_CTR)
nounce = b64encode(cipher.nonce).decode('utf-8')
if request.is_ajax():
nounce_ = self.nounce
msg_ = self.message
key = self.key
nounce = b64decode(nounce_)
ct = b64decode(msg_)
cipher = AES.new(key, AES.MODE_CTR, nounce=nounce)
msg_ = cipher.decrypt(ct)
mwssage = msg_.decode()
return JsonResponse({'message': message})
return render(request, 'chat/room.html')
The path to the decryption view was empty, after adding a path in the url it worked.
I am using auto submit in my forms.
Form gets automatically submitted when 10 digits are entered in the number field. This actually comes from USB RFID card reader upon scannig the card (It works as keyboard emulator).
I compare the number with the existing numbers in the database and then identify the user and perform some database transactions.
What happens is that the card reader reads the number multiple times, and due to that before all the database transactions complete for that input, another input is accepted (same number) within 1s and the consistency is lost before the first transaction is fully completed.
Kindly advise.
VIEWS.PY File:
#login_required
def markattdview(request):
# for val in studmst.objects.filter(ccownerid=request.user.id)
# TO DOooo
# select studid from attddetail where studentID= (select student ID from studmst where ccownerid = currentuser.ID)
# lastattd=attddetail.objects.filter()
# Showing last 10 users whose attendance have been marked by the logged in user
#
form_attd = markattdform()
attdsuccess = ""
identified_user = ""
identified_studname=""
loggedin_userid=request.user.id
loggedinuser=request.user.username
print("Logged in User : " + loggedinuser)
if request.method == 'POST':
studmstobj = studentmaster()
attdform = markattdform(request.POST)
unknown_cardno = request.POST.get('cardno')
if attdform.is_valid():
# form_attd.punchtime = datetime.datetime.now()
# attdform.save(commit=True)
obj = attdform.save(commit=False)
obj.punchtime = datetime.datetime.now()
attdsuccess = "Card not assigned. Kindly verify if the card has been registered."
#studlist = studmst.objects.filter(ccownerid = request.user.id)
#print (studlist[0])
# for usr in studlist(cardnumber = obj.cardno):
# Identifying the user who swyped the card
for usr in studentmaster.objects.filter(cardnumber = obj.cardno):
#WORKING FINE AFTER REMOVING WORKER AND REDISTOGO
#saving the details for identified User
identified_studname= usr.studentname
identified_cardno=usr.cardnumber
identified_studid = usr.studentid
punchdatetime=obj.punchtime
obj.studentname=identified_studname
obj.studentid_id=identified_studid
print('User Identified for card number '+ identified_cardno)
print( "Identified user - " + identified_studname)
databasetransactions(identified_studid,identified_studname,punchdatetime)
JAVASCRIPT FOR AUTO FORM SUBMISSION:
$(function () {
console.log(thetext);
$('#thetext').bind('change keyup', function () {
if ($(this).val().length >= 10) {
$('#Form').submit();
}
})
});
I would add two variables: formSubmitted and previousInput. And every time the length of the text is > 10, I would check if the form was submitted and also if previous input is equal to the current input.
When you submit the form - you set the value of formSubmitted to true.
And in this case your form will be submitted if it wasn't submitted already, or if the previous input is different than the current one which means that another text was entered and it should also be submitted.
And finally on ajax success or error you set the formSubmited to false back to allow new form submitions.
Remember that your form will be also submited if the current input is different than the previous input!
$(function () {
var formSubmitted = false;
var previousInput = null;
$('#thetext').bind('change keyup', function () {
if ($(this).val().length >= 10 && (!formSubmitted || previousInput != $(this).val())) {
var formData = new FormData($('#Form')[0]);
previousInput = $(this).val();
formSubmitted = true;
$.ajax(
url: '/endpoint.php',
method: 'POST',
processData: false,
contentType: false,
data: formData,
success: function(data, status, xhr) {
formSubmitted = false;
},
error: function(data, status, xhr) {
formSubmitted = false;
}
});
}
})
});
I developed an Angular application where the user can handle brands.
When creating/updating a brand, the user can also upload a logo. All data are sent to the DB via a REST API built using the Django REST Framework.
Using the Django REST Framework API website I'm able to upload files, but using Angular when I send data thu the API I get an error.
I also tried to encode the File object to base64 using FileReader, but I get the same error from Django.
Can you help me understanding the issue?
Models:
export class Brand {
id: number;
name: string;
description: string;
is_active: boolean = true;
is_customer_brand: boolean = false;
logo_img: Image;
}
export class Image {
id: number;
img: string; // URL path to the image (full size)
img_md: string; // medium size
img_sm: string; // small
img_xs: string; // extra-small/thumbnail
}
Service:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Brand } from './brand';
const endpoint = 'http://127.0.0.1:8000/api/brands/'
#Injectable()
export class BrandService {
private brands: Array<Brand>;
constructor(private http: Http) { }
list(): Observable<Array<Brand>> {
return this.http.get(endpoint)
.map(response => {
this.brands = response.json() as Brand[];
return response.json();
})
.catch(this.handleError);
}
create(brand: Brand): Observable<Brand> {
console.log(brand);
return this.http.post(endpoint+'create/', brand)
.map(response => response.json())
.catch(this.handleError);
}
get(id): Observable<Brand> {
return this.http.get(endpoint+id)
.map(response => response.json())
.catch(this.handleError);
}
private handleError(error:any, caught:any): any {
console.log(error, caught);
}
}
Error from the browser console:
"{"logo_img":{"img":["The submitted data was not a file. Check the
encoding type on the form."]}}"
Django Serializer:
class BrandSerializer(ModelSerializer):
is_active = BooleanField(required=False)
logo_img = ImageSerializer(required=False, allow_null=True)
class Meta:
model = Brand
fields = [
'id',
'name',
'description',
'is_active',
'is_customer_brand',
'logo_img',
]
def update(self, instance, validated_data):
image = validated_data.get('logo_img',None)
old_image = None
if image:
image = image.get('img',None)
brand_str = validated_data['name'].lower().replace(' ','-')
ext = validated_data['logo_img']['img'].name.split('.')[-1].lower()
filename = '{0}.{1}'.format(brand_str,ext)
user = None
request = self.context.get('request')
if request and hasattr(request, 'user'):
user = request.user
image_serializer_class = create_image_serializer(path='logos', filename=filename, created_by=user, img_config = {'max_w':3000.0,'max_h':3000.0,'max_file_size':1.5,'to_jpeg':False})
image_serializer = image_serializer_class(data=validated_data['logo_img'])
image_serializer.is_valid()
validated_data['logo_img'] = image_serializer.save()
old_image = instance.logo_img
super(BrandSerializer, self).update(instance,validated_data)
if old_image: # Removing old logo
old_image.img.delete()
old_image.img_md.delete()
old_image.img_sm.delete()
old_image.img_xs.delete()
old_image.delete()
return instance
def create(self, validated_data):
image = validated_data.get('logo_img',None)
print(image)
if image:
print(image)
image = image.get('img',None)
print(image)
brand_str = validated_data['name'].lower().replace(' ','-')
ext = validated_data['logo_img']['img'].name.split('.')[-1].lower()
filename = '{0}.{1}'.format(brand_str,ext)
user = None
request = self.context.get('request')
if request and hasattr(request, 'user'):
user = request.user
image_serializer_class = create_image_serializer(path='logos', filename=filename, created_by=user, img_config = {'max_w':3000.0,'max_h':3000.0,'max_file_size':1.5,'to_jpeg':False})
image_serializer = image_serializer_class(data=validated_data['logo_img'])
image_serializer.is_valid()
validated_data['logo_img'] = image_serializer.save()
return super(BrandSerializer, self).create(validated_data)
When posting a new brand to the server with files, I have three main choices:
Base64 encode the file, at the expense of increasing the data size by around 33%.
Send the file first in a multipart/form-data POST, and return an ID to the client. The client then sends the metadata with the ID, and the server re-associates the file and the metadata.
Send the metadata first, and return an ID to the client. The client then sends the file with the ID, and the server re-associates the file and the metadata.
The Base64 encoding will involve unacceptable payload.
So I choose to use multipart/form-data.
Here's how I implemented it in Angular's service:
create(brand: Brand): Observable<Brand> {
let headers = new Headers();
let formData = new FormData(); // Note: FormData values can only be string or File/Blob objects
Object.entries(brand).forEach(([key, value]) => {
if (key === 'logo_img') {
formData.append('logo_img_file', value.img);
} else {
formData.append(key, value);
});
return this.http.post(endpoint+'create/', formData)
.map(response => response.json())
.catch(this.handleError);
}
IMPORTANT NOTE: Since there's no way to have nested fields using FormData, I cannot append formData.append('logo_img', {'img' : FILE_OBJ }). I had change the API in order to receive the file in one field called logo_img_file.
Hope that my issue helped someone.
I am working on a python django web app in which I want to implement internationalization and auto translate the whole app into french or chinese.
I took reference from this site https://www.metod.io/en/blog/2015/05/05/django-i18n-part-1/
But whenever I try to run the app it shows this error:
500: ValueError at /en/get_dashboard_data/ The view
dashboard.views.getDashboardData didn't return an HttpResponse object.
It returned None instead.
And url get_dashboard_data is fetching data through ajax.
url(r'^get_dashboard_data/$', views.getDashboardData, name='getDashboardData'),
view
#login_required(login_url='/')
def getDashboardData(request):
dbname = request.user.username
if request.method == 'POST' and request.is_ajax():
if request.POST.get('action') == 'sale_chart_data':
data = DashboardData(dbname).getSaleChartData()
channel_list = data[0]
data_list = data[1]
print 123, data_list, channel_list
return HttpResponse(json.dumps({'channel_list':channel_list, 'data_list':data_list}), content_type='application/json')
if request.POST.get('action') == 'get_sale_numbers':
sale_data = DashboardData(dbname).getSaleNumbers()
return HttpResponse(json.dumps({'sale_number_data':sale_data}), content_type='application/json')
if request.POST.get('action') == 'get_inventory_numbers':
inventory_data = DashboardData(dbname).getInventoryData()
return HttpResponse(json.dumps({'inventory_data':inventory_data}), content_type='application/json')
if request.POST.get('action') == 'get_order_numbers':
order_data = DashboardData(dbname).getOrderData()
return HttpResponse(json.dumps({'order_data':order_data}), content_type='application/json')
if request.POST.get('action') == 'get_hourly_data':
order_data = DashboardData(dbname).getHourlyData()
sale_data = order_data[1]
count_data = order_data[0]
return HttpResponse(json.dumps({'sale_data':sale_data, 'count_data':count_data}), content_type='application/json')
if request.POST.get('action') == 'top_performers':
data = DashboardData(dbname).getTopPerformers()
inventory_count_dict = data[0]
current_month_dict = data[1]
last_month_dict = data[2]
current_quarter_dict = data[3]
current_year_dict = data[4]
channel_list = data[5]
return HttpResponse(json.dumps({'inventory_count_dict':inventory_count_dict,'current_month_dict':current_month_dict,'last_month_dict':last_month_dict,'current_quarter_dict':current_quarter_dict,'current_year_dict':current_year_dict,'channel_list':channel_list}), content_type='application/json')
if request.POST.get('action') == 'top_products':
product_data = DashboardData(dbname).getTopProducts()
return HttpResponse(json.dumps({'product_data':product_data}), content_type='application/json')
javascript
function getSaleChart(){
$.ajax({
url : "/get_dashboard_data/",
type : "POST",
data : {action:'sale_chart_data'},
success : function(response) {
channel_list = response.channel_list;
data_list = response.data_list;
c3.generate({
bindto: '#sale-chart-30-days',
data:{
x: 'dates',
xFormat: '%b %d',
columns: data_list,
colors:{
Flipkart: '#1AB394',
Paytm: '#BABABA'
},
type: 'bar',
groups: [ channel_list ]
},
axis: {
x: {
type: 'timeseries'
}
}
});
},
error : function(xhr,errmsg,err) {
toastr["error"]("Something Broke.", "Oops !!!.");
console.log(xhr.status + ": " + xhr.responseText);
}
});
}
This is why you should really practice more defensive programming. Though you insist that the request method is POST and it is ajax and the action is sale_chart_data one of the three isn't what you expect it to be.
Your function really should be like follows. It's plain old good practice.
def getDashboardData(request):
dbname = request.user.username
if request.method == 'POST' and request.is_ajax():
action = request.POST.get('action')
if action == 'sale_chart_data':
data = DashboardData(dbname).getSaleChartData()
....
...
# other if conditions here
else :
return HttpResponse(json.dumps({'message':'Unknown action {0}'.format(action)}), content_type='application/json')
else :
return HttpResponse(json.dumps({'message':'Only ajax post supported'}), content_type='application/json')
And then you ought to set break points and evaluate the request to figure out what exactly is happening in this particular request.
My guess would be that your JavaScript does indeed make a POST request to /get_dashboard_data/ but it receives a redirect response (HTTP 301 or 302) to /en/get_dashboard_data/ due to some kind of i18n middleware.
The browser follows the redirect, but the new request to /en/get_dashboard_data/ is a GET request.
Edit:
When following a redirect, the browser will always perform the second request as GET, there is no way to prevent that. You have several options to solve this:
make the initial request to the right application. This means you have to pass your i18n URL into your JavaScript instead of hardcoding it. You can add something like this to your template:
<script>var dashBoardDataUrl = "{% url "name-of-dashboard-url" %}"</script>
as your "actions" just get code, you could just accept a GET request and read the action from query paramters
Split that view into several smaller views that accept GET request so you have something that resembles a REST API.