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'})),
Related
Hi I'm wondering what the best practice is for creating a new model entry with a user based off the request in Django Rest Framework?
Models:
class Asset(models.Model):
user = models.ForeignKey(UserAccount, on_delete=models.CASCADE, related_name="assets")
name = models.CharField(max_length=200)
amount = models.DecimalField(max_digits=10, decimal_places=2)
Serializers:
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = '__all__'
Views
class CreateAssetView(generics.CreateAPIView):
serializer_class = AssetSerializer
<Doesn't seem to work, possibly since user isn't in the json>
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Basically I want to be able to send a POST request {name: 'myasset', amount: '50'} to this endpoint and have a new Asset saved with the User field obtain from the request. What is the best way to do this? Thanks
*** EDIT ***
Thought of a better solution:
class CreateAssetView(generics.CreateAPIView):
serializer_class = AssetSerializer
queryset = Asset.objects.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
However this means I must send a dummy user_id in the POST request from the front-end. I'm not sure how this can be avoided. Any suggestions highly welcome.
I do most often this thing using function-based views not class-based ones. :
Basically, that will also be able to send a POST request and will save the user who requested the post request.
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework.permissions import IsAuthenticated
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def perform_create(request, pk):
user = request.user
asset= Asset.objects.get(id=pk)
data = request.data
# Create Asset
asset = Asset.objects.create(
user=user,
name=user.first_name,
amount=data['amount '],
)
asset.save()
return Response('Asset Added')
And to return the data I create another view for the serialized data
where needed. I guess there would be other approaches I'm sure but
this one is much simple and easy to do.
Since Post "author" cannot be null then we need to provide a user,
one way to do this is to put the user instance in the request.data from the frontend...
the example below is assigning the user instance to request.data from the backend after the request is made!
...models.py
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
content = models.CharField(max_length=255)
...views.py
class PostCreate(CreateAPIView):
queryset = Post
serializer_class = PostSerializer
# override this method
def create(self, request, *args, **kwargs):
request.data['author'] = request.user.pk
return super().create(request, *args, **kwargs)
I am developing a website which is build on 2 servers: Django(back), ReactJS(front); (I have changed some titles to abstract names for the sake of convenience)
Model:
class Model(model.Model):
attr1 = models.CharField(max_length=500)
attr1 = models.CharField(max_length=500)
....
**author = models.ForeignKey(UserModel, on_delete=models.CASCADE)**
Custom Create View:
class ModelCreateAPI(APIView):
def post(self, request):
serializer = serializers.ModelSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
model_saved = serializer.save()
return Response({"success": "Model created successfully"})
Serializer:
class ModelSerializer(serializers.ModelSerializer):
class Meta:
model = Model
fields = '__all__'
On my React APP there is a form that sends HTTP request, including 'user token', that is stored in localStorage. However, 'author' of model should be an integer that represents pk. I have managed to convert token to pk:
>>> from rest_framework.authtoken.models import Token
>>> Token.objects.get(key='token').user_id
Is it an effective way of doing this operation?
What are your suggestions?
How I can implement HTTP POST request in way that it saves the model with correct author_id?
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'})
I want to do the following:
models.py
class MyModel(TimeStampedModel, models.Model):
name = models.CharField(max_length=100)
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
serializers.py
class MyModelSerializerCreate(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = (
'name',
)
And I would like to add as owner the current user in request.user.
Currently I am adding this in my view directly by uptading request.data with user and then pass the updated data to my serializer.
data = request.data
# Add owner to data
data["owner"] = request.user.pk
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
I would like to do this in my serializer directly but can't find a way to properly do it because it looks like data validation to me. Is this a good idea ? Should I keep this logic in my views or move it to my serializer ?
You can get a user from serializer context:
self.context['request'].user
It is passed from a method get_serializer_context which originally created in a GenericAPIView:
class GenericAPIView(APIView):
....
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
Inside a serializer you need to override a create method:
class MyModelSerializerCreate(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('name', )
def create(self, validated_data):
validated_data['owner'] = self.context['request'].user
return super(MyModelSerializerCreate, self).create(validated_data)
You could also override an update and delete methods if you need some special interactions with the User model.
Unfortunatly I dont have the reputation points to comment on #ivan-Semochkin post above, but should the last line not be:
return super(MyModelSerializerCreate, self).create(validated_data)
The solution from Ivan Semochkin did not work for me, as it never entered into the create() method of the serializer. As request.data field is immutable, you need to copy it and then extend it.
from django.http import HttpRequest
from rest_framework.request import Request
class MyModelViewSet(ModelViewSet):
def _extend_request(self, request):
data = request.POST.copy()
data['owner'] = request.user
request_extended = Request(HttpRequest())
request_extended._full_data = data
def create(self, request, *args, **kwargs):
request_extended = self._extend_request(request)
return super().create(request_extended, *args, **kwargs)
I have 2 models, representing an author his articles, the idea is an author can add articles freely (not that it matters for my problem, but in an append only matter).
class Author(models.Model):
name = ...
class Article(models.Model):
author = models.ForeignKey(Author, related_name='articles', on_delete=models.CASCADE)
title = ...
content = ...
timestamp = models.DateTimeField(auto_now=True)
My serializes are as follows:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = '__all__'
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
And I have a simple view:
#csrf_exempt
def author_articles(request, author_id):
try:
author = Author.objects.get(id=author_id)
except Author.DoesNotExist:
return HttpResponse(status=404)
if request.method == 'GET':
articles = author.articles.all()
serializer = serializers.ArticlesSerializer(articles, many=True)
return JsonResponse(serializer.data, safe=False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = serializers.ArticlesSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
Where the Url is defined:
url(r'^authors/(?P<author_id>[0-9]+)/articles/$', views.author_articles)
So a simple POST request to the URL: server:port/authors/1/articles/ with the body below works like a charm!
{
"author": 1,
"title": "foo",
"content": "bar"
}
What bugs me is that I provide the author id twice, once in the body and once in the path params. I can easily remove it from the path param and make it work, but what I really want is the opposite. I want the body to be without the id, and the id deduced from the path param.
I tried many many ways, and failed repeatedly. Would appreciate help.
I am using Django and Django-Rest-Framework over Postgres.
Thanks.
You could pass author_id to serializer save() method:
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = serializers.ArticlesSerializer(data=data)
if serializer.is_valid():
serializer.save(author=author.id)
One advice, Django Rest Framework allows you write less more code. You don't need regular Django views with JSONParser, JsonResponse etc.
Use APIView class or api_view decorator instead or even use viewsets it can reduce the function lines to 5-6