in DRF i have a some custom action that will do something to user model.user instances are all in state of is_active = False.Im trying to make something that turns the user to is_active = True. i made some a token model that has OneToOne to my user model.the function im trying to make is : if token that user put in the form equals to user.token then set user.is_active = True.im confused how to do that. I made my own serializer class :
class ActivateUserSerializer(serializers.ModelSerializer):
phonenumber = serializers.CharField()
token = serializers.CharField()
class Meta:
model = UserProfile
fields = ['phonenumber','token']
def get_token(self, obj):
request = self.context.get('request')
x = request.data['phonenumber']
obj = UserProfile.objects.get(phonenumber=x)
if request.data['token'] == obj.first_token:
obj.is_active = True
obj.save()
i know this is not .create() .or update() function.so this is how I reach so far.I dont know what view i should use for this functionality.
You could create a new POST endpoint in your API in order to get this custom action, for example:
api/users/<phone number>/activate
Then, in the view class, you can implement the action:
from rest_framework import status, viewsets
from rest_framework.decorators import detail_route
from rest_framework.response import Response
class UserView(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
# Use your own user serializer
serializer_class = UserSerializer
#detail_route(methods=['post', ])
def activate(self, request, phonenumber):
obj = UserProfile.objects.get(phonenumber=phonenumber)
# The POST request expects a token
if not request.data['token']:
return Response({'message': 'Token not provided'},
status=status.HTTP_400_BAD_REQUEST)
# Compare the token
elif request.data['token'] == obj.first_token:
obj.is_active = True
obj.save()
return Response({'message': 'User activated'})
# Maybe you could add an error code if you need
return Response({'message': 'User not activated'})
Related
how do I get self.request.user in DRF from a request in vue.js?
My request from .vue:
async LoadCase() {
this.case = await fetch(
`${this.$store.getters.getServerUrl}/casedetail/${this.slug}`
).then(response => response.json())
}
My class in views.py:
class DetailCase(RetrieveAPIView):
queryset = Case.objects.all()
serializer_class = CaseDetalSerializers
lookup_field = "slug"
My class in serializers.py:
class CaseDetalSerializers(serializers.ModelSerializer):
possible_open = serializers.SerializerMethodField()
class Meta:
model = Case
exclude = ("id","slug")
def get_possible_open(self,case):
request = self.context.get('request')
print(request)
print(request.user)
What I get from print:
<rest_framework.request.Request: GET '/api/v1/casedetail/rofl'>
AnonymousUser
Right now I'm getting the AnonymousUser for some reason, and I should be getting admin. What do I have to do to get it?
Hello you should use a method of authentication for your API in order to get a user otherwise it will be an AnonymousUser object.
This is a simple example of jwt authentication
https://simpleisbetterthancomplex.com/tutorial/2018/12/19/how-to-use-jwt-authentication-with-django-rest-framework.html
I have a class named Customer:
class Customer(models.Model):
name = models.CharField(max_length=250)
status = models.IntegerField()
And the serializer is:
class CustomerSerializer(serializers.ModelSerilizer):
class Meta:
model = Customer
fields = '__all__'
Now how can I change/update only the status field using POST method. I am using function base view here.
I want to receive value such as:
{
"status": 1
}
This would have been way easier if you were using class based views. You can easily create an UpdateStatusView that updates RetrieveUpdateAPIView send a patch request.
However, since you're using function based views, I'll still recommend you use a PATCH request rather than a POST request, this give better self documentation.
def update_status_request(request, id):
if request.method == 'PATCH':
customer = Customer.objects.get(pk=id)
customer.status = request.data.get('new_status')
customer.save()
return JsonResponse({'message': 'Status has been updated'}, status=200)
You might also wanna do some extra validation and try...except.
why do you want user post method to update the data, since you are update you can user patch
class CustomerUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ('status',)
from rest_framework.decorators import api_view
from rest_framework import response
#api_view(methods=['post', "patch"])
def api_function(request, *args, **kwargs):
change instance logic according to u
instance = Customer.objects.get()
if request.method == "POST":
# post is used for create data so i did for that for update use path
serializer = CustomerSerializer(data=request.data)
else:
serializer = CustomerUpdateSerializer(instance=instance, data=request.data)
if serializer.is_valid(raise_exceptions=True):
serializer.save()
return response.Response(serializer.data)
You can use viewset for this purpose.
View:
from rest_framework import viewset
class CustomerViewSet(viewset.ModelViewSet):
serializer_class = CustomerSerializer
Url:
path('customer/update/<int:pk>', CustomerViewSet.as_view({'post': 'update'})),
I'm working on a project using django-rest-framework. In my API view, an authenticated user can create other users. But, only five. Then if there are five users registered by one user, I want to send him in the response that hit the limit. Then, I need to get on my serializer the authenticated user but, I can't find a way to pass it from my ModelViewSet to my serializer.
This is my code:
View:
class ChildUserViewSet(viewsets.ModelViewSet):
serializer_class = ChildUserSerializer
queryset = User.objects.all()
authentication_classes = (
TokenAuthentication,
)
permission_classes = (
IsAuthenticated,
)
def perform_create(self, serializer):
account_group = self.request.user.userprofile.get_account_group
mobile_number = serializer.data.get('mobile_number')
password = serializer.data.get('password')
user = serializer.save()
user.set_password(password)
user.save()
# Generate user profile
UserProfile.objects.create(
user=user,
mobile_number=mobile_number,
user_type=CHILD,
related_account_group=account_group,
)
Serializer:
class ChildUserSerializer(serializers.ModelSerializer):
mobile_number = serializers.CharField()
class Meta:
model = User
fields = (
'first_name',
'last_name',
'email',
'password',
'mobile_number',
)
def validate(self, data):
"""
Check that the start is before the stop.
"""
# Get authenticated user for raise hit limit validation
def validate_email(self, value):
if User.objects.filter(email=value):
raise serializers.ValidationError("This field must be unique.")
return value
def create(self, validated_data):
username = generate_unique_username(
u'{0}{1}'.format(
validated_data['first_name'],
validated_data['last_name'],
)
)
user = User(
username=username,
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user.save()
return user
Then, in the def validate(self, data) function of my serializer, I want to get the currently authenticated user.
How can I pass the request.user from my APIView to my serializer?
I found an even easier way of accomplishing this! It turns out that Rest Framework's GenericAPIView base class (from which all of Rest Framework's generic View classes descend) includes a function called get_serializer_context():
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
As you can see, the returned context object contains the same request object that the View receives. This object then gets set when the serializer is initialized:
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
Thus to access the user who made the request, you just need to call self.context['request'].user from within your Serializer's validate_ function:
class TemplateSerializer(serializers.ModelSerializer):
def validate_parent(self, value):
print(self.context['request'].user)
return value
class Meta:
model = Template
And the best part is that you don't have to override anything in your ModelViewSet, they can stay as simple as you want them to:
class TemplateViewSet(viewsets.ModelViewSet):
serializer_class = TemplateSerializer
permission_classes = [IsAdmin]
In your views when you initialize serializer like
serializer = ChildUserSerializer(data=request.DATA,context={'request':request})
,send a context which contains request.Then in Serializers inside function call
request=self.context['request']
Then you can access request.user.
You can pass additional context to your serializer with serializer = ChildUserSerializer(data, context={'request': request}). You can then access the authenticated user via request.user within your serializer validation method.
In djangorestframework > 3.2.4 the rest_framework.generic.GenericAPIView class includes the http request by default in the serializer context.
So inside your serializer you can access it by: self.context['request'] and the user self.context['request'].user
So your ChildUserSerializer will look like:
class ChildUserSerializer(serializers.ModelSerializer):
mobile_number = serializers.CharField()
....
def validate(self, data):
"""
Check that the start is before the stop.
"""
# Get authenticated user for raise hit limit validation
user = self.context['request'].user
# do something with the user here
def validate_email(self, value):
if User.objects.filter(email=value):
raise serializers.ValidationError("This field must be unique.")
return value
...
How would I add the auth token to the userSeralizer?
This is my serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username')
And then in my views the url:
#api_view(['POST', 'DELETE'])
def create_user(request):
"""
API endpoint to register a new user
"""
model = User
serializer_class = UserSerializer
username, password = request.POST['username'], request.POST['password']
try:
user = User.objects.create_user(username, username, password)
except IntegrityError:
user = User.objects.get(username=username, email=username)
# the users token, we will send this to him now.
token = Token.objects.get(user=user)
if request.method == "POST":
serializer = UserSerializer(user)
return Response(data)
I think it would be nice to have the token in the serializer, or not?
From a security standpoint, auth tokens should not be passed around in the serializer. If your User view can be seen by anyone, then anyone could to impersonate any user without much trouble.
Tokens are meant to be returned only after successful login, not when an user is created. This is why most sites require Users to sign in just after the account was created.
But for the sake of the question, there are several ways to add items to serializers.
First, is a little hacky but doesn't require custom models
# Not adding context would raise a DeprecationWarning in the console
serializer = UserSerializer(user, context={'request': request})
data = serializer.data
data['token'] = token
return Response(data)
Last but not least, is a bit more elegant but requires a custom User class. However you could use it in your app models.
# in models.py inside your User model
def get_my_token(self):
return Token.objects.get(user=user)
my_token = property(get_my_token)
and then in the serializer class add the field with the token (remember to add it to the fields attribute in your meta class)
class UserSerializer(serializers.ModelSerializer):
token = serializers.Field(source='my_token')
class Meta:
model = User
fields = ('id', 'username', 'token')
I'm trying to go with-the-grain using Django TastyPie to update my models. I have an Identity model, acting as a wrapper around default Django user model:
class Identity(ProfileBase):
user = models.OneToOneField(User, related_name='identity')
avatar = models.ImageField(upload_to=avatar_upload_path, blank=True,
null=True)
I have my UserResource:
class UserResource(ModelResource):
class Meta:
resource_name = 'user'
queryset = User.objects.all()
fields = ['email', 'first_name', 'last_name']
include_resource_uri = False
And I have my IdentityResource:
class IdentityResource(ModelResource):
user = fields.ToOneField(UserResource, 'user', full=True)
class Meta:
resource_name = 'identity'
queryset = Identity.objects.select_related()
fields = ['user', 'avatar']
always_return_data = True
include_resource_uri = False
authentication = OAuthTokenAuthentication()
authorization = Authorization()
I'm currently successfully updating first_name, last_name using the ModelResource obj_update method within IdentityResource:
def obj_update(self, bundle, request, **kwargs):
print 'updating object'
bundle = self.full_hydrate(bundle)
bundle.obj.user = request.user
user = bundle.data['user']
bundle.obj.user.first_name = user['first_name']
bundle.obj.user.last_name = user['last_name']
return super(IdentityResource, self).obj_update(bundle, request, user=request.user)
I want to make a PUT request and optionally update any field on the user or identity models (first_name, last_name on user, or the avatar field on identity). I would rather not have to manually access each field from the bundle data and set them on models manually, as I have done above.
How can I do this naturally in TastyPie? Can someone explain a better approach to solving this problem? Any direction is GREATLY appreciated. :)
Here's my shot at providing an answer that attempts to leverage Tastypie as much as possible.
It is a little more generic than the OP's request (it will update any user, not just the one logged in). In the real world you would probably want to add some sort of authentication/authorization.
from tastypie.resources import ModelResource
from tastypie.authorization import Authorization
from django.contrib.auth.models import User
from myapp.account.models import Identity
class IdentityResource(ModelResource):
class Meta:
queryset = Identity.objects.all()
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
allowed_list_methods = ['get']
allowed_detail_methods = ['get','put']
authorization = Authorization()
def dehydrate(self, bundle):
identity_bundle = self.build_identity_bundle(bundle)
identity_bundle = IdentityResource().full_dehydrate(identity_bundle)
return identity_bundle
def obj_update(self, bundle, request, **kwargs):
user_bundle = super(UserResource, self).obj_update(bundle, request, **kwargs)
identity_bundle = self.build_identity_bundle(user_bundle)
IdentityResource().obj_update(identity_bundle, request)
return user_bundle
def build_identity_bundle(self, user_bundle):
identity_bundle = IdentityResource().build_bundle(
obj=user_bundle.obj.get_profile(),
data=user_bundle.data
)
return identity_bundle
What the example supports is:
GET a flattened User+Identity resource
PUT a flattened User+Identity resource, updating both models
You would want to register the UserResource in the API, and probably not the IdentityResource.
You could do something like this.
# Find all properties in user model.
properties = [prop for prop in bunder.obj.user if not prop.startswith('__')]
bundle_user = bundle.data['user']
# Find the property in bundle user and set it back on user if it exists.
for property in properties:
if property in bundle_user:
setattr(bundle.obj.user, property, bundle_user[property])
Maybe I'm missing the point but did you try a PATCH-method request? Tastypie will take all the sent attributes and update them in the database leaving all not-send attributes untouched.