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 :)
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])
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
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
How can i get/copy/access the array that is in Rails from Angular? I have an array of strings, called #array_of_names, in my def new function and would like to have the same array (#array_of_names) in my Angular function. What's the way to do that so i could have #array_of_names in my Angular side?
Rails file
def new
#account = Account.new
#array_of_names = read_names()
end
Angular file
(() => {
angular
.module('accounts')
.controller('AccountsController', AccountsController)
AccountsController.$inject = []
//testing function
function AccountsController () {
let vm = this;
vm.claim = "Hello, world!";
}
})()
You can accomplish this via AJAX. You need to set up a route in the Rails app that returns a JSON object for Angular to use.
Rails
Replace controller_name with your controller name
Controller (/app/controllers/controller_name.rb):
def get_names
render json: read_names()
end
Routes (/config/routes.rb):
get '/get_names' => 'controller_name#get_names'
Angular
function AccountsController($http){
var vm = this;
$http.get('/get_names').then(function(response){
vm.names = response.data;
});
}
AccountsController.$inject = ['$http'];
See this link to really understand what angular is doing: https://docs.angularjs.org/api/ng/service/$http
Here is a link explaining how to work with Rails and more complex JSON responses
https://www.leighhalliday.com/responding-with-json-in-rails