Django REST framework POST without insert - python

I'm building a Django REST API which has access to our existing database with existing users. The purpose of this API is allowing the upcoming mobile application to make requests.
I'm sending a post request to a view with custom authenticator to verify the sent account details.
My existing model:
class LogonAccount(models.Model):
id = models.BigIntegerField(primary_key=True)
email = models.TextField()
two_step_enabled = models.BooleanField()
password = models.TextField(blank=True, null=True)
username = models.TextField(unique=True, blank=True, null=True)
My View and Serializer
class LogonAccountViewSet(viewsets.ModelViewSet):
queryset = LogonAccount.objects.all().order_by('username')
serializer_class = LogonAccountSerializer
class LogonAccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = LogonAccount
fields = ('username', 'email')
Sending a post request to this endpoint with username and password in its body keeps returning me a bad request (400) with:
{
"username": [
"logon account with this username already exists."
],
"email": [
"This field is required."
]
}
Making the fields not required in the serializer just changes the error to database constraints (null value in column id)
class LogonAccountSerializer(serializers.HyperlinkedModelSerializer):
username = serializers.CharField(required=False)
email = serializers.CharField(required=False)
class Meta:
model = LogonAccount
fields = ('username', 'email')
I'm not trying to insert data, just trying to validate it.
What am I doing wrong or how do I stop it from trying to insert data?

The error you are getting is not because the DRF is trying to insert data but because of the validations available in the serializer, you are using. You are getting two validation errors:
email is required. Since in your model, the email field is not allowing blank or null values, so in the serializer, this is treated as a required field. So you need to handle it. Either you send that in the post request or make that field non-mandatory.
username is violating a unique constraint. In your model, you have set the username field as unique. So when a serializer is generated this unique field validation is added to that field.
If you want to see the serializer generated for your model serializer and all the fields and validations for the serializer, you can use the following code:
ser = LogonAccountSerializer()
print(repr(ser))
After the comment from #spoontech. The DB operation is performed because you are using viewsets.ModelViewSet. If you just want to use the serializer for validating data, you can use it this way:
serializer = LogonAccountSerializer(request.data)
is_valid = serializer.is_valid()
if is_valid:
# do something using serializer.validated_data
else:
# access errors using serializer.errors()

Related

Can I remove existing fields or add some more fields in the default User DB in Django?, Also can i login using email instead of username?

I want to create a database for my project using the default Django database 'user' and I found that Django has a primary key username and some fields are not required, if I can add more fields to the existing DB 'user' that would be great...
I want to log in using email instead of username and change some fields to required. Also, can I add a field 'address and 'mobile'?
Iam using Django 3.5.2
I have tried adding new fields using the following but still couldn't find a way to change the primary key and required fields
forms.py ->
class CustomerUserForm(forms.ModelForm):
class Meta:
model=User #django default model
fields=['first_name','last_name','password','email','username']
widgets = {
'password': forms.PasswordInput()
}
class CustomerForm(forms.ModelForm):
class Meta:
model= models.CRegistration
fields=['address','mobile','profile_pic']
models.py ->
class CRegistration(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
profile_pic = models.ImageField(upload_to='profile_pic/CustomerProfilePic/', null=True, blank=True)
address = models.CharField(max_length=100)
mobile = models.CharField(max_length=20) #null=True
status = models.BooleanField(default=False)
Yes you can customise user table in django. Also you can authenticate with email
Here is the below link, this article explains exactly what you need. Refer the django documentation for more
Django custom user with email authentication

error on Django drf ModelSerializer update method

I have a very strange error, this is my serializer class
class user_ser(ModelSerializer):
class Meta:
model = User
fields = '__all__'
depth = 1
whenever I send an API request to update user data, i got default values for is_staff and is_superuser
in the image below I send only email and password
example :
look what I got :(
this is the validated_data for the update method is :
I did not add is_staff or anything else to the request body, so why is that happening.
That's normal behavior; is_staff and is_superuser are default fields in Django used for authorization of admin users. You can view them as columns in the DB.
The real problem comes from using fields = '__all__' in the Meta class.
This is an anti-pattern since you can expose fields you didn't intend to expose. You should explicitly display fields that you intend to use.
Explicit is better than implicit.

How to POST an entity specifiying an integer foreign key in django rest framework

I am building a chat application with django rest framework and I m currently working on messages. This are my models:
from django.db import models
from django.contrib.auth.models import User
class Message(models.Model):
text = models.CharField(max_length=500)
datetime = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE);
I am using the Django auth User model. This is my ModelViewSet for the messages:
class MessageViewSet(ModelViewSet):
queryset = Message.objects.all()
serializer_class = MessageSerializer
And these are my serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username']
class MessageSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Message
fields = '__all__'
And this is my API:
The code I've written so far works really well for the GET functionally I want. I want for each message to get the username of the user it belongs to. But now I want the following thing: when I POST a new message, I want to be able to specify which user it belongs to by specifying the user's id. Right now I have only the "text" field in the POST section. I need to add a "user" field which takes in an integer (the user primary key) to specify which user the message belongs to. How should I refactor my code in order to do that?
Because you've overridden the user field and set it to read_only=True, you cannot set a user when you're creating/updating a model.
If you just need the user's username, I'd suggest you to add a username field into MessageSerializer directly instead:
class MessageSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username', read_only=True)
class Meta:
model = Message
fields = '__all__'
Now you'll get this payload instead:
{
"id": 1,
"user": 1,
"username": "timi",
...
And you should be able to set a user id now.

django login using class based for custom user

here is my user model.
class User (models.Model):
username = models.CharField(max_length=50)
# token = models.CharField(max_length=50)
email_id = models.EmailField(max_length=50)
password = models.CharField(max_length=50)
is_deleted = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
updated_at = models.DateTimeField(auto_now_add=True, blank=True)
and here is my views for creating user
class UserView(APIView):
def post(self,request):
try:
# RequestOverwrite().overWrite(request, {'token':'string'})
user_data = UserDetailSerializer(data=request.data)
if not(user_data.is_valid()):
return Response(user_data.errors)
user_data.save()
return Response("user created successfully",status=status.HTTP_201_CREATED)
except Exception as err:
print(err)
return Response("Error while creating user")
now what i want to do is to create a token when i post a user and that token is used later for login.
also i want to validate user if it exist in database then make user authenticate.
what should i do..?any suggestion
below is my serializers.py
class UserDetailSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','username','email_id','password','is_deleted','created_at','updated_at')
extra_kwargs = {
'password': {
'required':True,
'error_messages':{
'required':"Please fill this field",
}
}
}
First and foremost, it seems you are defining a custom user that is not in any way connected the Django auth user. It is a very very bad idea and will be difficult to get it work with most Django features. You can check out how to customize the existing user if you really need to.
As for user authentication, using the DRF Token Authentication, the flow is this way:
Client sends request to create user
If user is created successfully, it requests for an authentication token using the user's login and password
The backend verifies user's credentials and issues a token
Client makes subsequent requests with the token
If token expires or user logs out, repeat 2-4
Check out how to do these in DRF's TokenAuthentication documentation.
Your question is not very specific so I'm not sure what sort answer you are expecting but following these steps should get you going.
Token creation should be realized in model User. You can set default value to token field.
Add parameter default to User model token field:
token = models.CharField(default=tokenGenerator, max_length=50)
tokenGenerator should be some function that returns some generated token.
Secondly UserDetailSerializer should be edited to get token if it's necessary but not required.
class UserDetailSerializer(serializers.ModelSerializer):
# You can also provide this default function like in model field
# for this token field
# default=tokenGenerator
token = serializers.CharField(max_length=50, required=False)
class Meta:
model = User
fields = (..., 'token')
extra_kwargs = ...
Now your model gets generated token for newly created User.
User should be auto logged in after registration but it's not that simple.
You need to specify how do you want to communicate with API. You want token so i guess that front should request api and token should be used from cookies.
If you send request from browser eg. jQuery you need to get response with generated token and save it in cookies. Your post response in DRF:
return Response({'token': user_data.get('token')},status=status.HTTP_201_CREATED)
And now you have some more steps:
Attach token to every browser request which requests API
Create authentication class to validate every request checks for correct token
...
Now, if you read this you realize that it needs some work. If you know that and you intentionally want to create this, you need to create mentioned view authentication class.
Else try to use libs like oauth for django. That will provide you ready classes for token management.
Also as #Ken4scholars mentioned, try to use Django User model. It have good methods for validation etc. without make this done manually.

Manipulate Request data of TastyPie and Django API

I'm writing an application with django-tastypie and following are my models.py and resource.py files.
Models.py:
import uuid
from django.db import models
class User(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=50, null=False)
email = models.EmailField(max_length=254, null=False)
password = models.CharField(max_length=100, null=False)
role = models.CharField(max_length=16, default='basic', null=False)
def __unicode__(self):
return self.name, self.email
Resources.py:
from tastypie.resources import ModelResource
from tastypie.authorization import Authorization
from api.models import User
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
authorization = Authorization()
excludes = ['password']
#allowed_methods = ['get']
Now the thing is that whenever I hit an API end point from postman, the user is created directly. Now what I don't understand is that whether the request data goes into resources and then into database or directly into the database? Actually, the thing is that I need to apply some changes to the data before it is stored in the database, like hashing the password and then storing the object in the database. I'm new to django, so how can I achieve that?
Like in Flask, we can do something like:
#user.route('/users', methods=['POST'])
def create_user(user_id):
data = request.get_json(force=True)
# do all the changes we want
user = User(data)
db.session.add(user)
db.session.commit()
Now if any request comes at '/users' endpoint, we can get it's data in the 'data' variable and then whatever changes we want before storing in the database. But how to do that in django with tastypie.
Any help would be appreciated
If you have to massage data before entering into database then Tastypie has the notion of hydrate and dehydrate methods.
Check that. Here is reference hydrate and dehydrate
In every web framework the data that sent with the request passed to the api endpoint through some mechanism and the same thing happens in Tastypie (you can read about it in Tastypie documentation under Flow Through The Request/Response Cycle).
If you want to change the data that you sending/receiving read about Hydrate/Dehydrate, in your case you want to use dehydrate on user password but I recommend you to save the effort and instead use custom user model by inheriting from AbstractUser, that way you can get hashed password by default when User object is saved to your DB.

Categories