Django AJAX login without re-direct - django

Yes, there is a similar question without resolution to the actual question.
My users may be trying out the app and then decide to register for an account to save. Once registered I need to log them in without redirecting or reloading the page. I've got the registration working and logging them in is no problem, but what do I return to the client in order for the logged in session to be active in the browser? I'm not looking to add any other frameworks or libraries to the stack, please.
Django View (some omitted)
class CheckAuthTokenAjaxView(FormView):
form_class = AuthTokenForm
def post(self, *args, **kwargs):
form = self.form_class(self.request.POST)
u = get_user_model().objects.filter(email=email).first()
u.email_confirmed = True
u.save()
login(self.request, u)
# What should I be sending back?
return JsonResponse({"success": True}, status=200)
JS
Doc.Register.checkAuthTokenForm = function(event) {
event.preventDefault();
var form = document.getElementById('authtoken-form');
var data = new FormData(form);
d3.json('/users/ajax/checkAuthToken/', {
method: 'post',
body: data,
headers: {
// "Content-type": "charset=UTF-8",
"X-CSRFToken": Doc.CSRF,
}
})
.then(result => {
if (result.hasOwnProperty('errors')) {
// ...
} else {
// WHAT DO I DO HERE?
}
});
}

Related

Navigating to different page instead of Ajax call

I am trying to save a form via ajax as I don't want to reload the page. It is working not completely but it is updating the data except the image or video or any upload file we give.
but after updating it is coming back to the ajax page but the url is diiferent and success message is coming on that page.
I am sharing some of the logic but if more information is required, please let me know .
js code :
$(document).ready(function() {
$('#contentdataform').submit(function(e) {
e.preventDefault();
$.ajax({ // create an AJAX call...
data: $(this).serialize(),
type: 'POST',
url: 'updatecontent',
success: function() {
mess("Success"); //mess is a function to generate a disappearing message box.
},
});
return false;
});
});
function updatecontentform(){
console.log('starting');
document.getElementById('contentdataform').submit();
}
views.py
#csrf_exempt
def updatecontent(request):
print("--------------------")
if request.method == "POST":
id = request.session['content_id']
fm = ContentForm(request.POST, instance= Content.objects.get(pk = id))
print("fm")
print(fm)
if fm.is_valid:
print("valid form")
form = fm.save(commit=False)
form.save()
else:
print("Not Valid")
return JsonResponse("Success", safe= False)
the output should be the message on the same page but it is reflecting on the new page with url '127.0.0.1:8000/updatecontent'

Show loading gif until the django view performs the data processing and renders the template with this data

I have a django project where the page has multiple nav links representing different agents. On clicking any nav link, the urls.py redirects to nav specific view and the view needs to perform some processing to get the data needed to render the template. However as this is syncrhonous rendering it takes a long while to load data (in the order of 15-20s).
Below is my urls.py:
from django.urls import path
from . import views
app_name = 'agent'
urlpatterns = [
path('agent1/', views.agent1, name='agent1'),
path('agent2/', views.agent2, name='agent2'),
path('agent3/', views.agent3, name='agent3'),
path('agent4/', views.agent4, name='agent4'),
]
My views method looks as below:
def agent1(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return render(request, 'manager/data.html', {'agent_data': agent_data, 'agent_details': agent_details})
I am using the {{ agent_data.name }}, {{ agent_data.code }}, {{ agent_data.qty }} and {{ agent_data.price }} along with data from agent_details dictionary in my html to populate a table's rows. How should I change my view method, so that it loads the data via AJAX (javascript) in order to show a loading gif in the meantime and also provide me the data so that I can populate the table. Could someone help me with the Ajax code and the steps as I am new to this technology and not finding any help going through the online tutorials.
So for this to work with ajax, you'll need some javascript in manager/data.html which knows the url to fetch data from.
As an example, I've got an ajax setup which checks a given email address isn't already in use;
(function($) {
$(document).ready(function() {
var validateEmailURL = $section_user_signup.data('ajax-email-url');
function validateEmailUnique() {
var valid = true;
clearError($email);
// Fetch unique status of the provided email
$.ajax({
async: false,
url: validateEmailURL,
method: 'POST',
type: 'POST',
dataType: 'json',
data: {
'email': $email.val(),
'csrftoken': $form.find('input[name="csrfmiddlewaretoken"]').val()
},
success: function (response) {
valid = true;
},
error: function (response) {
setError($email, response["responseJSON"]["error"]);
valid = false;
}
});
return valid;
}
});
})(window.jQuery);
This javascript uses the data attribute of a div for the URL to check;
<div data-ajax-email-url="{% url 'account_ajax_validate_email' %}">
The view which the ajax call goes to looks like this;
def ajax_check_email_unique(request, *args, **kwargs):
"""
Return an JsonResponse to identify if an email is unique.
"""
if not request.is_ajax():
return HttpResponseBadRequest()
if request.is_ajax and request.method == "POST":
email = request.POST.get('email')
if email_address_exists(email):
return JsonResponse(
{
"error":
"Email address already exists. Click "
f"here "
"to login"
},
status=400
)
return JsonResponse(
{"email": email},
status=200
)
# some error occurred
return JsonResponse({"error": ""}, status=400)
The important thing for any view which will be used by javascript is that you return a JsonResponse.
So if I was you, I'd setup a new view for ajax, and that makes your existing one really simple;
def agent1_ajax(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return JsonResponse({
"agent_data": agent_data, "agent_details": agent_details
}, status=200)
def agent1(request):
return render(request, 'manager/data.html', {})
And as far as a loading gif goes, you'd need an element that contains the gif and then you can bind to the ajax event to show/hide;
$(document).ajaxStart(function() {
$("#loading").show();
});
$(document).ajaxStop(function() {
$("#loading").hide();
});

Cookies works in Postman , but not in browser

I created a login API using Django rest framework and then used session auth.
When i sent request via Postman , i get csrftoken and sessionid cookies.
and i was able to access content on backend.
OK fine.
But when i built small login form html and called that API for logging in. It worked.
I see COOKIES IN RESPONSE BUT COOKIES ARE NOT SET IN CHROME BROWSER.
Under Storage section in dev tools cookies are empty.
when i tried to access content(other views/apis) , i was not able to..
I think its because of Cookies are not being stored in browser..
Been on this like 5 days. Please can Someone explain about cookies not being saved.?
View.py
class Login(APIView):
authentication_classes = [SessionAuthentication,]
def post(self, request, format=None):
username = request.POST.get("username", "")
print(request.session)
password = request.POST.get("password", "")
user = authenticate(request,username=username,password=password)
if user is not None:
login(request,user)
print(user)
return Response('Yes')
else :
return Response('No')
class List(APIView):
authentication_classes = [SessionAuthentication,]
permission_classes = [IsAuthenticated,]
def get(self, request, format=None):
return Response("Ark")
My Axios Request for login :
let s = this;
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
myHeaders.append("Authorization", "Basic cjox");
myHeaders.append("Access-Control-Allow-Credentials","*");
var urlencoded = new URLSearchParams();
var requestOptions = {
method: 'POST',
credentials: 'same-origin',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
axios.post("http://127.0.0.1:8000/api/login/",urlencoded,{headers:myHeaders},{withCredentials: true})
.then(res=>{
console.log(res.headers);
})
My other request :
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
myHeaders.append("Access-Control-Allow-Credentials","*");
var urlencoded = new URLSearchParams();
var requestOptions = {
method: 'GET',
credentials: 'same-origin',
headers: myHeaders,
redirect: 'follow'
};
axios.get("http://127.0.0.1:8000/api/d/",{headers:myHeaders},{withCredentials: true});

How to Sync Django HTTP Login Session with Websocket Session/Cookie?

I am using Django DjangoChannelsGraphqlWs which is Graphene version of Django Channels. (https://github.com/datadvance/DjangoChannelsGraphqlWs) It allows me to transfer data using graphql style. I wrote a login login on my mutation schema.
class Login(graphene.Mutation):
class Arguments:
email = graphene.String()
password = graphene.String()
ok = graphene.Boolean()
user = graphene.Field(UserType)
def mutate(root, info, email, password, **kwargs):
ok = False
user = authenticate(info.context, username=email, password=password)
if user is not None:
login(info.context, user)
update_or_create_user_login_info(info=info)
ok = True
return Login(ok=ok, user=user)
else:
return Login(ok=ok)
And I wrote my client side Websocket using Apollo-client like this:
class WebSocketService {
static instance = null;
callbacks = {};
static getInstance() {
if (!WebSocketService.instance)
WebSocketService.instance = new WebSocketService();
return WebSocketService.instance;
}
constructor() {
this.socketRef = null;
this.connect();
}
connect() {
const client = new SubscriptionClient(BASE_URL + '/graphql/', {
reconnect: true,
});
const link = new WebSocketLink(client);
const cache = new InMemoryCache();
this.socketRef = new ApolloClient({
link,
cache,
});
}
query = (query, variables={}, context={}, fetchPolicy='no-cache', errorPolicy='all') =>
this.socketRef.query({
query: gql`${query}`,
variables,
context,
fetchPolicy,
errorPolicy
})
mutate = (mutation, variables={}, context={}, fetchPolicy='no-cache', errorPolicy='all') =>
this.socketRef.mutate({
mutation: gql`${mutation}`,
variables,
context,
fetchPolicy,
errorPolicy
})
}
const WebSocketInstance = WebSocketService.getInstance();
export default WebSocketInstance;
Lastly, here is my consumer.
class MyGraphqlWsConsumer(channels_graphql_ws.GraphqlWsConsumer):
"""Channels WebSocket consumer which provides GraphQL API."""
schema = schema
send_keepalive_every = 60
I attempted to log in using the WebSocketInstance. However, Django's login function fails when I hand over info.context and authenticate(info.context, user) parameters. It throws error saying "types.SimpleNamespace' object has no attirbute 'META'". After stumbling upon with the error, I gave up with logging in with a websocket and decided to just use axios and normal http request.
But here is another issue. The session and cookies are not synced. When I use axios and normal http request, the login itself does work well, but websocket connection does not reflect the logged in session/cookies.
I found that it takes some time to reflect the change and it happens after disconnecting and reconnecting the websocket.
How should I sync the login information?
try overing the graphl_jwt
class ObtainJSONWebToken(graphql_jwt.JSONWebTokenMutation):
user = graphene.Field(UserType)
session = graphene.String()
#classmethod
def resolve(cls, root, info, **kwargs):
user=info.context.user
if user:
login(info.context,user)
session=info.context.session
session.save()
if session.session_key:
#print(session.session_key)
return cls(user=user,session=session.session_key)
else:
raise Exception('try it again')
in mutation
class Mutation(graphene.ObjectType):
token_auth=ObtainJSONWebToken.Field()

Getting 403 on POST

I am learning to work with Django Rest Framework and following the tutorial. I have create a simple index based on the tutorial, that works for GET, but not for POST:
#api_view(['GET','POST'])
def game_list(request):
if request.method == 'GET':
games = Game.objects.all()
serializer = GameSerializer(games, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = GameSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I set the default settings to AllowAny:
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny'
]
}
but I still get a HTTP 403 when I try to POST anything, using the Firefox RESTClient. I read that I have to add a X-CSRFToken header and cookie for this to work, but I do not have those.
From documentation:
By default, a ‘403 Forbidden’ response is sent to the user if an incoming request fails the checks performed by CsrfViewMiddleware. This should usually only be seen when there is a genuine Cross Site Request Forgery, or when, due to a programming error, the CSRF token has not been included with a POST form.
Also, as stated if the official documentation, CSRF is enabled by default, and you need to add a X-CSRFToken in your AJAX requests.
Here is the code from the documentation:
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
Take in mind that the documentation suggest to use ajaxSetup method from jquery, which is not a recommended way to do it because it can alter the way that others scripts uses the ajax function, so it's better to add the specific CSRF's code in your custom JS code like this:
$.ajax({
method: 'POST',
url: 'your.url.com/',
beforeSend: function(xhr, settings) {
if (!WU._csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
success: function(msg)
{}
});
Reference: https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax