Django - simplejson response - javascript

I'm using jQuery autocomplete plugin http://www.devbridge.com/projects/autocomplete/jquery/ to provide search suggestions in my web application where I want to send the response in json format.
Django views.py for sending the suggestions response:
def keywords_suggestions(request):
if request.is_ajax():
suggestions = []
q = request.POST.get('q')
try:
g = KeywordsModel.objects.filter(keyword__startswith=q).order_by('count')
except KeywordsModel.DoesNotExist:
return HttpResponse("")
else:
for i in range(0,len(g)):
global suggestions
suggestions.append(g[i].keyword)
to_json = {
"query": q,
"suggestions": suggestions
}
return HttpResponse(simplejson.dumps(to_json), mimetype='application/json')
Django models.py:
class KeywordsModel(models.Model):
keyword = models.CharField(max_length=40, blank=False)
count = models.IntegerField(max_length=20)
def __unicode__(self):
return self.keyword
jQuery code:
$("#add-keywords").keyup(function() {
$('#add-keywords').autocomplete({
serviceUrl:'/keywords_suggestions',
minChars:3,
maxHeight:220,
width:280,
zIndex: 9999,
params: { q: $('#add-keywords').val() },
onSelect: function(value, data){ $('#add-keywords').val(value); },
});
});
I'm getting this error when I type on the #add-keywords text box.
Request URL:http://127.0.0.1:8000/keywords_suggestions/?q=web&query=web
Request Method:GET
Status Code:500 INTERNAL SERVER ERROR
UPDATE
ValueError at /keywords_suggestions/
The view information.views.keywords_suggestions didn't return an HttpResponse object.
UPDATE-2
I'm having doubt in the suggestions variable, maybe global suggestions will have the problem. Am I doing it right?
Could anyone guide me to make it work?
UPDATE-3
<input type="text" id="add-keywords" name="add-keywords" title="e.g. Web developer, Javascript, Musician, Jazz" />
How to get the value of #add-keywords text box in the Django views.py. Does this work q = request.POST.get('add-keywords')?
Thanks!

the judgement request.is_ajax() returns False

The condition branches
try:
g = KeywordsModel.objects.filter(keyword__startswith=q).order_by('count')
except KeywordsModel.DoesNotExist:
return HttpResponse("")
else:
...
also could fail as ValueError if, for example, request.POST.get('q') results None
Plus, try '/keywords_suggestions/', note the suffix slash, instead of '/keywords_suggestions' in the serviceUrl:'/keywords_suggestions', line

Related

GET request is Sent Twice; Once With the Query String Inside the URL and Once Without It

My issue is that whenever the button const loadOldPlayer is clicked, it sends two GET requests, as shown in the screenshot below. What this results in is the wrong template being rendered (showsPlayer.html should be what is rendered but instead it just renders playerView.html). I can't figure out why this is happening, so any help is appreciated. Below the screenshot is my code.
let playerName
const loadOldPlayer = document.getElementById('playerLoader');
const enterProfile = (usedToLoad) => {
console.log(playerName)
if (usedToLoad) {
playerName = document.getElementById('loadPlayerName').value
};
const playerData = {
playerName: playerName
};
const JSONdata = JSON.stringify(playerData);
fetch(`/profile?tags=${JSONdata}`, { method: "GET" }).then((response) => {
if (response.ok) {
document.getElementById('loaderLabel').innerHTML = "Loading Player"
}
else {
alert("Something bad happened.");
};
});
};
loadOldPlayer.addEventListener("click", enterProfile.bind(true));
from flask import Flask, render_template, request
from static.SNEKfiles import SpaceShipGame
import json
game_app = Flask(__name__)
#game_app.route('/')
#game_app.route('/home')
def home():
return render_template("HTMLPage1.html")
#game_app.route('/profile', methods=['GET'])
def profile():
if request.method == 'GET':
playerName = request.args.get('tags')
if playerName != None:
print("got the name")
return render_template("showsPlayer.html")
else:
print("here is issue")
return render_template("playerView.html")
if __name__== "__main__":
game_app.run(debug=True, host='127.0.0.1', port=7777)
Yes my HTML files are badly named, I'll probably get around to fixing that. Eventually.
I'm inexperienced with this, so I might be completely wrong here. Anyway, try adding the event listener with
loadOldPlayer.addEventListener("click", function() {
enterProfile(true);
});
instead of
loadOldPlayer.addEventListener("click", enterProfile.bind(true));
I think the bind(true) might be causing the empty querystring.
If this doesn't work, could you add some debug output?
Also, the return render_template("showsPlayer.html") successfully returns the html, but the client never actually uses it. Try using
fetch(`/profile?tags=${JSONdata}`, { method: "GET" }).then((response) => {
if (response.ok) {
document.getElementById('loaderLabel').innerHTML = "Loading Player"
return response.text()
}
else {
alert("Something bad happened.");
}
}).then((html) => {
console.log(html) // should show response html
document.open();
document.write(html); // writes response html to body
document.close();
})
Document.write() is considered bad practice, but it is the only way (that I know of, at least) to dynamically replace the entire page.
I edited this answer because I didn't notice your use of the .bind(true) earlier.
The event listener expects a function reference, which is what Quackers had you implement. Similar to Quackers solution, but using the shorter arrow function form :
loadOldPlayer.addEventListener("click", () => enterProfile(true));
That way, you are passing a function reference, and that function, when called by the event, just call your function with the value set to "true" as you expect.
Your original code was using .bind() which creates a new function with this set to the value you provided to .bind. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind.
By passing enterProfile.bind(true) to addEventListener, you ended up passing a new enterProfile function to handle the click event (that new function having the this set to true, which had no effect for you).
When the event fired, the browser actually was passing the Event object to the function, not the value true (see event callbacks https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#the_event_listener_callback).
At that point, the if (usedToLoad) was entirely dependant on what Event data was passed and if that evaluated to a truthy value or not.

Field 'id' expected a number but got <SimpleLazyObject: <User: username>>

I have been stuck on this error for some time and I can't wrap my head around the problem or what it even means.
I found some answers but none really solved my issue.
Here is a brief desciption of what I do:
In Javascript, I call this function with an int as a parameter such as:
function follow (user_id) {
// retrieves post for current selection
fetch(`/follow/${user_id}`, {
method: 'PUT',
body: JSON.stringify({
follow: true
})
})
}
My url path, from url.py, is as follow:
path('follow/<int:user_id>', views.follow, name="follow")
finally, in views.py, this function is called:
def follow(request, user_id):
user = User.objects.get(id = user_id)
if request.method == "PUT":
data = json.loads(request.body)
if data.get("follow") is not None:
followed = Followed(
user_id = user.id,
followed_by_id = request.user,
)
followed.save()
return HttpResponseRedirect(reverse("index"))
else:
return HttpResponseRedirect(reverse("index"))
I have tried a few different approaches such as removing the .value but I keep getting the following error:
Field 'id' expected a number but got <SimpleLazyObject: <User: username>>
I check along the way and the ID is an int all the way until it is passed to the model to be saved.
I am using the abstractUser model.
Let me know if more information is needed.
Kind regards,

DFR simple jwt get authToken from a user instance instead of username/password

I have a simple chat app, authentication in it works exaclty as whatsapp
Get phone number => if doesn't exist create one else skip => send validation code and set it as "phone_code" field in User model => finally remove the "phone_code" if validated
The app is built in React Native with Rest framework as the API, I'm new to this and I'm struggling to get the authentication token without the password. i use djangorestframework-simplejwt
my register view:
#api_view(('POST',))
def register(request):
if request.method == 'POST':
serializer = UserSerializer(data=request.data)
if not serializer.is_valid():
if 'is not valid' in serializer.errors['phone_number'][0]:
return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)
phone_number = serializer.initial_data['phone_number'].replace(' ', '')
try:
user = User.objects.get(phone_number=phone_number)
except User.DoesNotExist:
user = User.objects.create_user(
phone_number=phone_number, username=phone_number)
user.phone_code = randint(99999, 999999)
user.save()
TokenObtainPairView()
return Response(serializer.data, status.HTTP_200_OK)
# todo send validation code, I will handle later
my Login view (Chich validates for the validation code)
#api_view(['POST',])
def loginuser(request):
if request.method == 'POST':
phone_number = request.data.get('phone_number')
try:
user = User.objects.get(phone_number=phone_number)
if int(request.data.get('phone_code')) == user.phone_code and user.phone_code:
user.phone_code = None
user.save()
#!!!!!!!!!!!!!!!!!!!NOW HOW CAN I GET THE JWT AUTHENTICATION TOKEN AND SEND IT TO MY REACT NATIVE APP?!
return JsonResponse({'phone_number': phone_number}, status=200)
else:
return JsonResponse({'error': "Invalid code"}, status=400)
except Exception as error:
return JsonResponse({'error': error}, status=500)
when the user validates his phonenumber, how can I send the jwt format auth token with the Response?
urls.py:
path('api/token/', users_views.ObtainToken.as_view(), name='token_obtain_pair'),
my Custom obtainToken view:
class ObtainToken(TokenObtainPairView):
permission_classes = (AllowAny,)
serializer_class = MyTokenObtainPairSerializer
Also I found out that when I was using Postman if I send an empty password the system will give a the authentication token. I would appreciate any help, thanks
You should base your code on the existing views and serialiazer from rest_framework_simplejwt : using TokenObtainPairView is a good start, keep that.
Then in your seriliazer_class, MyTokenObtainPairSerializer, you should use TokenObtainSerializer
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
self.user = User.objects.get(phone_number=phone_number)
# Do the verification with the phone_code here, if error, return a response with an error status code
refresh = self.get_token(self.user)
data['refresh'] = text_type(refresh)
data['access'] = text_type(refresh.access_token)
return data

Return two responses in Flask/JavaScript

I have a Flask/JavaScript application wherein I take a form's inputs and pass them to a Flask app to retrieve distance information from the GoogleMaps API and subsequently return the resulting JSON to JavaScript.This works fine for a single instance of an origin/destination.
I want to receive two origin/destination inputs and return both to my JavaScript but cannot figure out how to do that. I'm still learning, but am under the impression I can't simply return two values in a single function so I'm hoping someone can take a look at what I have and tell me what the best approach would be to get the JSON for both back to JavaScript.
#app.route("/", methods=['GET', 'POST'])
def home():
if request.method == 'POST':
# form inputs
origin = request.form.get('origin')
destination = request.form.get('destination')
current_home = request.form.get('current_home')
future_home = request.form.get('future_home')
# current traffic conditions set to now
departure = int(time.time())
# params we pass to the url
current_params = {
'origins': origin,
'destinations': destination,
'mode':'driving',
'units':'imperial',
'departure_time' : departure,
'traffic_model':'best_guess',
'avoid':'tolls'
}
future_params = {
'origins': future_home,
'destinations': destination,
'mode':'driving',
'units':'imperial',
'departure_time' : departure,
'traffic_model':'best_guess',
'avoid':'tolls'
}
# api call
current_url = 'https://maps.googleapis.com/maps/api/distancematrix/json?'+ urllib.urlencode(current_params)
future_url = 'https://maps.googleapis.com/maps/api/distancematrix/json?'+ urllib.urlencode(future_params)
current_response = requests.get(current_url)
future_response = requests.get(future_url)
# return json
return jsonify(current_response.json())
return jsonify(future_response.json())
return render_template('index.html')
if __name__ == "__main__":
app.run(debug=True)
You need to wrap both values in a dict and then return the dict.
payload = {
"current_response": current_response,
"future_response": future_response
}
return jsonify(payload)

Dynamic EventSource with flask and javascript

I am currently building a tool using flask that does various actions using ssh. One of those actions is using DD to copy from X to Y.
I currently have the following javascript set up on my page
<script>
var source == new EventSource("/dd");
source.onmessage = function(event){
$('.determinate'.css('width', event.data+'%')
if(event.data == 100){
source.close()
}
}
Which calls the following flask generator which parse's the stdout of DD to return a % value for the current progress.
#app.route('/dd')
def progress():
def generate():
ssh.SSHClient('127.0.0.1', username, password)
chan = ssh.get_transport().open_session()
chan.settimeout(10800)
try:
ssh.do('dd if=/dev/sda of=/test.img')
while data:
data = chan.recv_stderr(1024)
try:
yield "data: " + str(data) + "\n\n\"
return Response(generate(), mimetype='text/event-stream')
The above is pseudo code, but the things i want to be able to change are the DD command (dd if=/dev/sda of=/test/img) from variables i get from the form that triggers this page, as well as the hostname from the ssh.connect function with request.remote_addr.
When i try to replace '127.0.0.1' with request.remote_addr i get an out of context error.
Is there anyway to pass flask/jinja2 variables such as {{ image.path }} to my generator view?. The pseudo code i want would be this, where hostname, and dd is dynamic ( changes are in curly brackets )
#app.route('/dd')
def progress():
def generate():
ssh.SSHClient({{ request.remote_addr }}, username, password)
chan = ssh.get_transport().open_session()
chan.settimeout(10800)
try:
ssh.do('dd if={{ device }} of={{ image.path }}')
while data:
data = chan.recv_stderr(1024)
try:
yield "data: " + str(data) + "\n\n\"
return Response(generate(), mimetype='text/event-stream')
Just figured it out, didn't read the last paragraph of the docs.
If You want to keep the context of the previous request in the generator just change
return Response(generate(), mimetype='text/event-stream')
to
return Response(stream_with_context(generatE()), mimetype='text/event-stream')
For passing data i just used the "session" object to pass data and then remove it when im done using it.

Categories