I will be using Django only as the backend. The front end will be done using React and no django templates. I am using django-rest-framework to create a rest api for my website.
I made a serializer for the user.
class CustomUserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = (
'id', 'email', 'password', 'username', 'first_name', 'last_name', 'date_of_birth', 'gender', 'mobile_number'
)
extra_kwargs = {
'password': {'write_only': True},
'id': {'read_only': True}
}
def create(self, validated_data):
user = CustomUser.objects.create(
email=validated_data['email'],
username=validated_data['email'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
date_of_birth=validated_data['date_of_birth'],
gender=validated_data['gender'],
mobile_number=validated_data['mobile_number']
)
user.set_password(validated_data['password'])
user.save()
return user
class CustomUserViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = CustomUserSerializer
In the browser, when I go to /custom/users/ I can view the users. I can also create new users which after successful registration returns back the user. Also it works if I use httpie/curl.
(djangoweb) vagrant@precise32:~$ http --json POST http://55.55.55.5/custom/users/ email="[email protected]" password="terminal2123" username="t223erm" first_name="te2er" last_name="mi2nal" date_of_birth=1992-12-12 gender=2 mobile_number=66222666666336
It creates and returns the new user object.
So I made a form to register a user which I am not serving from the django server:
<form action="http://55.55.55.5/custom/users/" method="post" id="register-form">
<input type="text" placeholder="email" name="email"/>
...
...
<button id="post">Register</button>
</form>
And ajax to post the form.
// using the javscript Cookies library
var csrftoken = Cookies.get('csrftoken');
function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$('#post').click(function(event) {
event.preventDefault();
var $form = $('#register-form');
var data = $form.serialize();
$.ajax({
type: "POST",
url: "http://55.55.55.5/custom/users/",
data: JSON.stringify(data),
sucess: function() { console.log("Success!"); },
contentType: "application/json; charset=utf-8",
dataType: "json",
crossDomain:false,
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
});
});
Now after if I click the button, the problem starts:
event.preventDefault()
the page is automatically loaded to the url of the django server (i.e., http://55.55.55.5/custom/users/).How can I handle the post to the django server using django-rest-framework? I didn't find any helping material for this problem. Could you please guide me how to? Thank you.
You can use csrf_exempt
for the registration and login functions. As an example, here how you can create the registration and login APIs. See how my login API returns the token. See http://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication.
I tried to edit my code to replace with your model names, but I did not test it, so you may need to fix any typos.
class AccountViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = CustomUserSerializer
def get_permissions(self):
if self.request.method in permissions.SAFE_METHODS:
return (permissions.IsAuthenticated(),)
if self.request.method == 'POST':
return (permissions.AllowAny(),)
return (permissions.IsAuthenticated(), IsAccountOwner(),)
@csrf_exempt
def create(self, request):
'''
When you create an object using the serializer\'s .save() method, the
object\'s attributes are set literally. This means that a user registering with
the password \'password\' will have their password stored as \'password\'. This is bad
for a couple of reasons: 1) Storing passwords in plain text is a massive security
issue. 2) Django hashes and salts passwords before comparing them, so the user
wouldn\'t be able to log in using \'password\' as their password.
We solve this problem by overriding the .create() method for this viewset and
using Account.objects.create_user() to create the Account object.
'''
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
password = serializer.validated_data['password']
confirm_password = serializer.validated_data['confirm_password']
if password and confirm_password and password == confirm_password:
user = CustomUser.objects.create_user(**serializer.validated_data)
user.set_password(serializer.validated_data['password'])
user.save()
return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
return Response({'status': 'Bad request',
'message': 'Account could not be created with received data.'
}, status=status.HTTP_400_BAD_REQUEST)
class APILoginViewSet(APIView):
@csrf_exempt
def post(self, request, format=None):
data = JSONParser().parse(request)
serializer = LoginCustomSerializer(data=data)
if serializer.is_valid():
email = serializer.data.get('email')
password = serializer.data.get('password')
if not request.user.is_anonymous():
return Response('Already Logged-in', status=status.HTTP_403_FORBIDDEN)
user = authenticate(email=email, password=password)
if user is not None:
if user.is_active:
login(request, account)
serialized = UserSerializer(user)
data = serialized.data
# Add the token to the return serialization
try:
token = Token.objects.get(user=user)
except:
token = Token.objects.create(user=user)
data['token'] = token.key
return Response(data)
else:
return Response('This account is not Active.', status=status.HTTP_401_UNAUTHORIZED)
else:
return Response('Username/password combination invalid.', status=status.HTTP_401_UNAUTHORIZED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def get(self, request, format=None):
data_dic = {"Error":"GET not supported for this command"}
return Response(data_dic, status=status.HTTP_400_BAD_REQUEST)
You can see a full working example at https://github.com/dkarchmer/django-aws-template (disclaimer, that's my code).