Django API to create new user - python

I'm trying to create user creation API, but I'm not sure if I'm doing this ok because I get something like this:
Am, I doing something wrong? Because I doubt this should look like this, this is my code:
serializer.py
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'password', 'email')
views.py
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from .serializer import *
#api_view(['POST'])
#permission_classes((AllowAny,))
def user_creation(request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Like #Mohit Harshan said you need to add:
#api_view(['POST','GET'])
to your view.
When you use the Rest Framework explorer, it is by default making a GET request, so it will always get a complaint if that method is not available. However, in this case your POST methods will still work.
Typically for user generated creation, the GET method will return a simple form that the user can fill in and then submit as a POST request, creating the user.

OK, problem solved, I wanted to know if POST view should look like on photo, and it should, I just need do write in Content field all my model fields by using dictionary

Related

Django REST User Creation/Authentication

This question is based on the one here. I am setting up a Django REST Framework for my web app and am trying to set up User accounts. Based on the REST documentation, they put all of their account code in their example in the main project directory and a separate application so did that as well. Here is what I have:
urls.py
from django.contrib import admin
from django.urls import include, path
from django.conf.urls import url
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register('users', views.UserViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
url('', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = User.objects.create(
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = User
# Tuple of serialized model fields (see link [2])
fields = ( "id", "username", "password", )
views.py
from rest_framework import viewsets, permissions
from rest_framework.generics import CreateAPIView
from django.contrib.auth.models import User
from .serializers import UserSerializer
# Create your views here.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
class CreateUserView(CreateAPIView):
model = User
permission_classes = [
permissions.AllowAny
]
serializer_class = UserSerializer
I have tried using the Boomerang REST Client in Chrome to POST data to this API, but it always returns a 403 Error saying "Invalid username/password." Specifically I am POSTing to http://127.0.0.1:8000/users/create/ with a Query String and 2 parameters: username and password. I also tried sending it as JSON and it returned the same. Any help would be appreciated.
It doesn't look like CreateUserView was registered in your urls.py. You should be able to register it and access it normally. I think this should work for you:
urlpatterns = [
...
url(r'^users/create/', views.CreateUserView.as_view()),
]
That said, I'd like to suggest adding an extra action for your UserViewSet instead:
# Create your views here.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated
#action(methods=['post'], detail=False, permission_classes=[permissions.AllowAny])
def register(self, request, *args, **kwargs):
# This logic was taken from the `create` on `ModelViewSet`. Alter as needed.
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Then you should be able to post via /users/register/. You can also specify your own url name and path on the decorator.
Maybe you are posting in the wrong url, try POST the same on http://127.0.0.1:8000/users/,
because ModelViewSet adds POST, PATCH, PUT, DELETE and GET methods automatically.
Also because you are asking for authentication (permission_classes = [permissions.IsAuthenticated]), you should send the headers for this in the request. There is a tutorial for this in the DRF site (https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/)
based on django-rest-framework documents it's better to use viewset for create user api. therefor you need to send a POST request to http://127.0.0.1:8000/api-auth/users and no need to CreateUserView function.
But if you want to have a custom user create api do you need something like below:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = User.objects.all()
serializer_class = UserSerializer
#action(detail=True, methods=['post'], permission_classes=[permissions.AllowAny])
def create_user(self, request, pk=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
To have custom serializers in your ViewSet you can specify them in get_serializer_class function in your ViewSet like this:
class UserViewSet(viewsets.ModelViewSet):
# example viewset
def get_serializer_class(self):
if self.action == 'list':
return ListUserSerializer
elif self.action == 'create':
return CreateUserSerializer
elif self.action == 'update':
return UpdateUserSerializer
return DetailUserSerializer

Django function based view allow is_active user whereas ClassBasedView does not allow is_active user

I have 2 views. One is function based and the other is class based. When I call the function based view with an is_active user, django does not throw any errors but when I call the class based view it returns 403 error.Any help will be highly appreciated. Thanks
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import api_view, permission_classes
#require_POST
#permission_classes([IsAuthenticated,])
def test_function_based_view(request):
return JsonResponse({
'success': "True",
})
class TestClassBasedView(APIView):
permission_classes = (permissions.IsAuthenticated,)
def post(self, request):
return JsonResponse({
'success': "True",
})
Django Version 1.11.21
It is possible that the problem is not that the class based view is returning a 403 error, but rather that the function based view is not checking the permissions correctly. This might be caused because #require_POST does not take the permissions into consideration.
Replace #require_POST in your function based view with #api_view(('POST',)).

How to do validation in django serializers?

I have 2 custom validation functions created using packages from PyPI, I want to inlcude them in my serializers.py in my django project before converting it to JSON using rest. Problem is i dont know where or how to put the functions in such that the code will run through it. Here is my code:
enter image description here(this is how im adding users right now, using the model's fields ive registered)
Here is my code:
/* serializers.py */
import re
import phonenumbers
from rest_framework import serializers
from phonenumbers import carrier
from validate_email import validate_email
class basicSerializer(serializers.Serializer):
emailplus = serializers.EmailField()
country = serializers.CharField(max_length=2)
phone_number = serializers.CharField(max_length=100)
def validate_emailplus(self):
email = self.validated_data.get("email")
if not validate_email(email, check_mx=True, verify=True):
raise serializers.ValidationError("Invalid email")
return email
/* views.py */
from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import basic
from .serializers import basicSerializer
class basicList(APIView):
def get(self, request):
basic1 = basic.objects.all()
serializer = basicSerializer(basic1, many=True)
return Response(serializer.data)
def post(self):
pass
As you can see I am not using models.py anymore and serializers.py as some sort of model with the given email and phone fields. In order for my function to work (which is not at the moment), it needs to use get() method from the entered data and do validate_email to know if the email exists. Please tell me what changes should be made, i do not know if the problem is with views.py which still uses models.py or if i should register the serializer as a model?
To run your validations, you must call serializer.is_valid(), however, that requires data to be passed to the serializer not instances. The logic behind this is that drf validates data coming in not data already stored in your DB
Correct logic
Considerations
It looks like you are implementing a list view, but you are validating the email address, which is probably not what you intended to do in the first place. I am guess you want to validate email on create.
You can make use of drf's generic views and mixins such as GenericViewSet, ListModelMixin, and ListModelMixin
I think you have a type in validate_emailplus where you try to get the field email while the serializer declares it as emailplus
You seem not to be following PEP-8 (style guide for Python)
serializers.py
import re
import phonenumbers
from rest_framework import serializers
from phonenumbers import carrier
from validate_email import validate_email
class BasicSerializer(serializers.Serializer):
emailplus = serializers.EmailField()
country = serializers.CharField(max_length=2)
phone_number = serializers.CharField(max_length=100)
def validate_emailplus(self):
email = self.validated_data.get("emailplus")
if not validate_email(email, check_mx=True, verify=True):
raise serializers.ValidationError("Invalid email")
return email
views.py
from rest_framework import mixins, viewsets
class BasicViewSet(
viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.CreateModelMixin,
):
queryset = Basic.objects.all()
serializer_class = BasicSerializer
For a better understanding of how viewset and mixins work, I recommend checking their implementation
Validation in Admin site
From the screenshot you added, it looks like you are trying to validate in the admin site, for that consider the below code:
models.py
class Basic(models.Model):
...
def clean(self):
if not validate_email(self.email, check_mx=True, verify=True):
raise ValidationError("Invalid email")
This works because Django admin generates forms based on your models and then the forms call full_clean() on the model, which calls clean()

(Django) Send POST ID back to the Client

I'm new to Django and I'm trying to create a somewhat basic API. But one thing that has been bugging me is how to create a callback function for when certain (asynchronous) events occur.
For example, one of my simplest cases is for when the client sends a POST request. I would like to send back to him the ID of that request in the DB. How would I do that?
My code mostly follows William S. Vincent's Django for APIs book with minor modifications.
The most important parts are:
models.py:
from django.db import models
class SegmentcliApi(models.Model):
request_id = models.AutoField(primary_key = True)
database = models.CharField(max_length = 100)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __str__(self):
return f'DB Request to {self.database}: created at {self.created_at}'
serializers.py:
from rest_framework import serializers
from .models import SegmentcliApi
class SegmentcliApiSerializer(serializers.ModelSerializer):
class Meta:
fields = (
'request_id',
'database',
'created_at',
'updated_at',
)
model = SegmentcliApi
views.py:
from rest_framework import generics
from .models import SegmentcliApi
from .serializers import SegmentcliApiSerializer
class SegmentcliApiList(generics.ListCreateAPIView):
queryset = SegmentcliApi.objects.all()
serializer_class = SegmentcliApiSerializer
class SegmentcliApiDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = SegmentcliApi.objects.all()
serializer_class = SegmentcliApiSerializer
In your views.py, more specifically, in the CreateApiView or ListCreateApiView classes, you can override the create method such that you can get a specific response from post request.
The create method comes from the CreateModelMixin class, which is inherited by the ListCreateAPIView, and which is in turn inherited by your specific Django model.
So follow the steps below:
from rest_framework import status
from rest_framework.response import Response
class SegmentcliApiList(generics.ListCreateAPIView):
queryset = SegmentcliApi.objects.all()
serializer_class = SegmentcliApiSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
# Original Response (inside the `CreateModelMixin` class)
# return Response(
# serializer.data,
# status=status.HTTP_201_CREATED,
# headers=headers
# )
# We will replace the original response with this line
return Response(
{'id': serializer.data.get('request_id')},
status=status.HTTP_201_CREATED,
headers=headers
)
# serailizer.data is the created object in a JSON format
This works for me. If it is not working for you, tell me what you're getting in comments. If it works for you, please accept the answer.
I would suggest starting to look at Django Forms and specifically Model Form, which is what I think SegmentcliApiSerializer is supposed to be. Then, I'm not sure how your urls.py is set up to work with your app, but in your template, the form would be something like:
<form action="{% url 'name_of_your_url_to_get_to_view_method' %}" method="POST">
{% csrf_token %}
{{ form }}
<button type="submit">BUTTON</button>
</form>
and view.py would be something like this:
def yourViewMethod(request):
if(request.method == "POST"):
form = SegmentcliApiSerializer(request.POST)
if(form.is_valid()):
form.save()
savedId = request.POST.get("request_id")
return render(request, "yourFolder/yourHtmlFile.html", {"savedId": savedId})
else:
form = SegmentcliApiSerializer()
return render(request, "yourFolder/yourHtmlFile.html", {"form": form})
and then, in whatever yourHtmlFile.html is, you can use
{{ savedId }}
somewhere to display it. I think that's what you're asking.
EDIT: Fixed so it got the value correctly.
I can't comment cause I don't have enough reputation but, with respect to #marsonfire's answer, if you want to have only simple JSON response you can use JsonResponse instead of render.
So, you'd have something like return JsonResponse({"savedId": savedId})

Get Authenticated user from token in Django Rest Framework

I am new in Django and I have managed to build a small API using DRF. I have my angular.js client end posting user auth details and DRF returns a token which looks like this:
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
Based on the tutorial, I am supposed to retrieve the details from request.user
But I don't know where to do this. I find it confusing since it doesn't give a good example. Anyone with an idea on how go around it? Your input is highly appreciated.
Below is the code of my view and serializer.
from serializers import ExampleSerializer
from models import Example
from rest_framework import viewsets
class ExampleViewSet(viewsets.ModelViewSet):
"""
Example api description
"""
queryset = Example.objects.all()
serializer_class = ExampleSerializer
Serializer
from models import Example
from rest_framework import serializers
class ExampleSerializer(serializers.ModelSerializer):
class Meta:
model = Example
fields = ('id', 'field_one', 'field_two', 'created_at', 'updated_at')
depth = 1
Keeping in mind that I am also new to Angular and DRF...
If you are already receiving the token, then on the angularjs side, you need to be including the token in the headers of your subsequent requests. Perhaps like this abbreviated code from the authentication request:
$http({auth request code here}).then(function(response){
var token = response.headers().token
$http.defaults.headers.common['Authorization'] = 'Token ' + token;
});
In your ViewSet you would likely want
authentication_classes = (TokenAuthentication,)
along with whatever permission_classes are relevant.
If you are including the Token in the Angular http request, then I believe you can reference the user with request.user, like perhaps
def list(self, request):
queryset = SomeObject.objects.filter(owner=request.user)
Or, here is another use (User model is django.contrib.auth.models.User):
class UserView(RetrieveAPIView):
model = User
serializer_class = UserSerializer
def retrieve(self, request, pk=None):
"""
If provided 'pk' is "me" then return the current user.
"""
if request.user and pk == 'me':
return Response(UserSerializer(request.user).data)
return super(UserView, self).retrieve(request, pk)
In my case, I am trying to test my API with an API REST Client. When I put the Header in the configuration, it works.
Authorization: Token <<token>>

Categories