I was making this simple BMR calculator app the problem is when I submit I don't want to go to any other page I want to show the results in the same page instead of having to go to another page.
In my views.py
def bmrvalue(age, height, weight, gender):
if gender == 'm':
BMR = (10 * weight) + (6.25 * height) - (5 * age) + 5
else:
BMR = (10 * weight) + (6.25 * height) - (5 * age) - 161
return BMR
def MyBmr(request):
if request.method == "POST":
form = BmrForm(request.POST)
if form.is_valid():
age = form.cleaned_data['age']
height = form.cleaned_data['height']
weight = form.cleaned_data['weight']
gender = form.cleaned_data['gender']
mybmr = bmrvalue(age,height,weight,gender)
return render (request, 'resultes.html', {'bmr':mybmr })
else:
form = BmrForm()
return render (request, 'home.html', {'form': form})
Any ideas on how I can achieve that?
The front end code required for this (place in your HTML) notice that I said required pretty much other than web sockets this is the way to do this:
<script>
function calculateResultsAndPost(){
const gender = document.getElementById('gender')
const weight = document.getElementById('weight')
const height = document.getElementById('height')
const age = document.getElementById('age')
const BMR = (10 * weight ) + (6.25 * height) - (5 * age ) + gender.isChecked ? 5 : - 161
// if you really need this on the server
fetch('/my-python-api-endpoint/bmr', {
method: 'POST',
body: {body:
{
BMR: BMR,
otherData: otherData,
weight: weight,
gender: gender.isChecked,
age: age
}
})
}
</script>
The logic in this for needs to move to JavaScript including all the math there seems to be no reason for serverside interaction here at all, but in case you need it those requests need to happen not using the
The only way to get data to the backend without making the page fully refresh is through JavaScript (whether new WebSocket(), XMLHttpRequest()/fetch()). If you submit a form with an action and method it will refresh the page there are no if ands or buts about this (well you can be really hacky and place that in an iFrame and have the iFrame refresh).
The backend Django code required:
In urls.py
from django.urls import path
from . import views
app_name = 'bmr'
urlpatterns = [
path('/bmr', views.IndexView.as_view(), name='index'),
]
In views.py. All this is a vague approximation
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, JsonResponse
from django.urls import reverse
from django.views import generic
from .models import User
class IndexView(generic.ListView):
def post(self, request, *args, **kwargs):
"""Accepts XMLHttpRequests"""
user = User.create(processRequest(request.body))
return JsonResponse(user.toJSON())
# or more simply:
def post(self, request, *args, **kwargs):
user = User.create(processRequest(request.body))
return JsonResponse(user.toJSON())
From the Django docs:
HttpRequest.body¶ The raw HTTP request body as a byte string. This is
useful for processing data in different ways than conventional HTML
forms: binary images, XML payload etc. For processing conventional
form data, use HttpRequest.POST.
You can also read from an HttpRequest using a file-like interface. See
HttpRequest.read().
Related
I'm using DRF as my backend API in conjunction with React.
I am making a GET request using fetch and then populating a react table with the data provided by DRF.
The issue is that when I first load the page or when I manually refresh it, theres a significant delay for the data to be fetched. I've tried limiting to 5 items but it still always takes 2 seconds.
However, if I make the same request after the page has fully loaded the speed is normal. I've added a gif to illustrate in case I didn't explain it properly. Once the role is updated in the DB, the same GET request is done again to repopulate the table.
My views, models and serializers:
#api_view(['GET'])
def getUserData(request):
items = User.objects.all()
# enabling pagination because api_view doesn't have it by default
paginator = LimitOffsetPagination()
result_page = paginator.paginate_queryset(items, request)
# creating 1 serializer instance for paginated and 1 for full
serializer_page = UserSerializer(result_page, many=True)
serializer_full = UserSerializer(items, many=True)
# if limit is not specified, return all object data
if 'limit' not in request.query_params:
return Response(serializer_full.data)
else:
return Response(serializer_page.data)
class Role(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='roles')
assigned_role = models.CharField(max_length=100)
class RoleSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = ['user', 'assigned_role']
class UserSerializer(serializers.ModelSerializer):
roles = RoleSerializer(read_only=True, many=False)
class Meta:
model = User
fields = '__all__'
Javascript code in case it's relevant:
useEffect(() => {
const fetchUsers = (num) => {
fetch(`http://127.0.0.1:8000/api/users/?limit=${num}`)
.then(response => response.json())
.then(data => {
console.log('Data loaded in useEffect')
dataObj = data
setUsers(dataObj)
setLoading(true)
})
}
fetchUsers(20)
}
, [searchDone])
So I made a django ajax that everytime there is a new message the notification object that I add to my user models will be +1, the problem is that I dont know why is giving me this error:
django.contrib.auth.models.User.DoesNotExist: User matching query does not exist.
here is my code:
views.py:
def count_notification(request):
user = request.user
obj = user.profile.objects.get(pk=1)
obj.notifications = obj.notifications +1
obj.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
urls.py:
path('messages/notification/', count_notification)
html file js
// Add the notification val
$.get('notification/')
models.py:
class ProfileImage(models.Model):
"""
Profile model
"""
user = models.OneToOneField(
verbose_name=_('User'),
to=settings.AUTH_USER_MODEL,
related_name='profile',
on_delete=models.CASCADE
)
avatar = models.ImageField(upload_to='profile_image')
notifications = models.FloatField(default='0')
thank you for the help
obj = user.profile.objects.get(pk=1)
i think your app is dealing with many users, each user has its own profile with its own notification count, so why you are fixing the id / pk to 1, you need to get the related profile, not hard code the id
try this code below
def count_notification(request):
user = request.user
user.profile.notifications += 1
user.profile.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
and change
notifications = models.FloatField(default='0')
to
notifications = models.IntegerField(default='0')
it does make more sens since notifications count is an integer not float nand don't forget to rerun migrations
Update
i guess you have not defined a custom User model in settings.py in this case you need to make some changes in models.py
refer to https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#reusable-apps-and-auth-user-model
from django.contrib.auth import get_user_model # get the default User model
User = get_user_model()
class ProfileImage(models.Model):
"""
Profile model
"""
user = models.OneToOneField(
verbose_name=_('User'),
# to=settings.AUTH_USER_MODEL, # you don't need this since you didn't
# defined a custom User model in
# setting.py
to = User, # HERE
related_name='profile',
on_delete=models.CASCADE
)
avatar = models.ImageField(upload_to='profile_image')
notifications = models.IntegerField(default='0') # change the field type
Update 2
from django.contrib.auth import get_user_model
User = get_user_model()
..
def count_notification(request):
# user = request.user
user = User.objects.filter(id=request.user.id).first() # HERE
user.profile.notifications += 1
user.profile.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
I am trying to use django rest framework to create instances of a Bookmark model for different types of content, for example Book.
The code:
models.py
class Bookmark(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
api.py
from django.contrib.contenttypes.models import ContentType
from rest_framework import viewsets, mixins, serializers, permissions
from .models import Bookmark
class BookmarkSerializer(serializers.ModelSerializer):
class Meta:
model = Bookmark
fields = ('id', 'user', 'content_type', 'object_id')
class BookmarkViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
queryset = Bookmark.objects.all()
serializer_class = BookmarkSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
content_type = self.request.query_params.get('content_type')
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
object_id = self.request.query_params.get('object_id')
serializer.save(user=self.request.user, content_type=ct, object_id=object_id)
def get_queryset(self):
items = Bookmark.objects.filter(user=self.request.user)
content_type = self.request.query_params.get('content_type', None)
if content_type:
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
items = items.filter(content_type=ct)
object_id = self.request.query_params.get('object_id', None)
if object_id:
items = items.filter(object_id=object_id)
return items
get_queryset part is fine. But perform_create fails when I try to create a new Bookmark:
var item = new Bookmark({'content_type': 'books.book', 'object_id': self.book_id});
item.save();
The response:
{"user":["This field is required."],"content_type":["Incorrect type. Expected pk value, received unicode."]}
It is not clear to me how I am supposed to do this. I would appreciate any feedback.
You should post your model definition as well, but the error you are getting is saying that you also need to pass a user argument to Bookmark and that you should be passing a pk value (aka an integer that is the id of the object you're pointing to) for content_type.
I can't say anything about the error message concerning the user, but here is my 2 cents about the second one.
You do not simply give a path to your model as a string to the content_type key.
Apparently
, you have to use the get_for_model method which is passed as parameter a model (The model 'Book', not an instance of it), and returns an object of type ContentType. This object's id is what you should pass as a value.
Both previous answers pointed me in the right direction. In the end, the solution was not to override perform_create, but create function:
def create(self, request, *args, **kwargs):
content_type = request.data['content_type']
if content_type:
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
request.data['user'] = request.user.id
request.data['content_type'] = ct.id
#object_id already passed as id in request.data
A further explanation can be read here:
https://github.com/encode/django-rest-framework/issues/3470
I have just started Django and what I was trying to implement $http.post method of angular to post form data to my Django database,and my further plan was to update the view to display the results without page refresh.so what i thought to post data in django db and then my view function will return a jsonresponse,of that posted data which i can use to update the view using $http.get method.
But the problem is occuring with me is whenever i post data it is not able to post the data and returning an empty json response.
Here is my codes which i am working on:-
urls.py
from django.conf.urls import url
from . import views
app_name='demo'
urlpatterns=[
url(r'^$',views.index,name="index"),
url(r'^add_card/$',views.add_card,name="add_card")
]
views.py
from django.shortcuts import render
from django.http import HttpResponse,JsonResponse
from .forms import CardForm
from .models import Card
# Create your views here.
def add_card(request):
saved = False
if request.method == 'POST':
#print('hi')
form = CardForm(request.POST)
#print('hi')
if form.is_valid():
#print('hi')
card = Card()
#print('hi')
card.content = form.cleaned_data['content']
#print('hi')
saved = True
card.save()
#print('hi')
return JsonResponse({'body':list(q.content for q in Card.objects.order_by('-id')[:15])})
else:
return HttpResponse(
json.dumps({"nothing to see": "this isn't happening"}),
content_type="application/json"
)
def index(request):
return render(request,'demo/index.html',{'form':CardForm()})
controller.js
var nameSpace = angular.module("ajax", ['ngCookies']);
nameSpace.controller("MyFormCtrl", ['$scope', '$http', '$cookies',
function ($scope, $http, $cookies) {
$http.defaults.headers.post['Content-Type'] = 'application/json';
// To send the csrf code.
$http.defaults.headers.post['X-CSRFToken'] = $cookies.get('csrftoken');
// This function is called when the form is submitted.
$scope.submit = function ($event) {
// Prevent page reload.
$event.preventDefault();
// Send the data.
var in_data = jQuery.param({'content': $scope.card.content,'csrfmiddlewaretoken': $cookies.csrftoken});
$http.post('add_card/', in_data)
.then(function(json) {
// Reset the form in case of success.
console.log(json.data);
$scope.card = angular.copy({});
});
}
}]);
My models.py:-
from django.db import models
# Create your models here.
class Card(models.Model):
content = models.TextField()
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.content
My forms.py-
from django import forms
from .models import Card
class CardForm(forms.ModelForm):
class Meta:
model = Card
fields = ['content']
You have some problems in your view.py code.
You need to create a new object in your database from the request data
You need to return the new data as a response
if form.is_valid():
new_content = form.cleaned_data['content']
card = Card.objects.create(content=new_content)
return JsonResponse(
list(Card.objects.all().order_by('-id').values('content')[:15]),
safe=False
)
That should return a list of the content value of your first 15 objects in your Card table after creating a new object in that table by the content provided, IF your form is valid.
Also your CardForm should be defined as follows:
class CardForm(forms.ModelForm):
class Meta:
model = Card
fields = ('content',)
Finally, your $http.post call is asynchronous, which means that when the .then is reached, there is a probability (almost a certainty) that the post request has not been resolved yet, thus your json.data is empty. To solve this problem:
$http.post('add_card/', in_data)
.then((json) => {
// Reset the form in case of success.
console.log(json.data);
$scope.card = angular.copy({});
});
A far better read and solutions on the asynchronous-to-synchronous calls are: ES6 Promises - Calling synchronous functions within promise chain, How do you synchronously resolve a chain of es6 promises? and Synchronous or Sequential fetch in Service Worker
Good luck :)
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)