alter data before update in django rest framework - django

I've looked around for answers but they say to use update() and it doesn't work for me
After the user updates an entity, I want to change it's data before saving it
N.B: the same thing is done for creating it by overriding the method perform_create() and it works.
Here's my code:
The update view
class CommentUpdateAPIView(generics.UpdateAPIView):
serializer_class = CommentModelSerializer
permission_classes = [permissions.IsAuthenticated]
The serializer
class CommentModelSerializer(serializers.ModelSerializer):
class Meta:
model = Comment # the model to get fields from
fields = [
'id',
'user',
'content',
'timestamp',
]
Thank you

Thanks to neverwalkaloner,
Here is the solution:
in my UpdateAPIView I overrid the method perform_update() of Generic API Views see the docs here GenericAPIView
And finally did this:
class CommentUpdateAPIView(generics.UpdateAPIView):
serializer_class = CommentModelSerializer
permission_classes = [permissions.IsAuthenticated]
queryset = Comment.objects.all()
def perform_update(self, serializer):
serializer.save(last_update_time = datetime.now())

Related

No data is being returned when I use an inherited Django Rest Framework serializer? It just returns an empty dictionary

I have a serializer that is meant to act as a template for other ModelSerializers.
class CountryBasedModelSerializer(ModelSerializer):
def __init__(self, data, context):
assert 'country' in self.Meta.fields
class Meta:
model = Country
fields = ()
I want to use it with this, which is the actual serializer which will be called.
class CountryBasedProjectSerializer(CountryBasedModelSerializer):
class Meta:
model = Project
fields = ('id', 'country', 'name')
I want to use it with this inherited viewset:
class CountryBasedViewset(viewsets.ModelViewSet):
queryset = None
serializer_class = CountryBasedModelSerializer
def get_queryset(self):
return self.queryset.filter(country_pk=self.request.data["country"])
And this is the actual viewset that will be called:
class CountryProjectBasedViewset(CountryBasedViewset):
queryset = Project.objects.all()
Is there anything that I am clearly doing incorrectly?
Just define the serializer_class for CounteryProjectBasedViewSet as below
class CountryProjectBasedViewset(CountryBasedViewset):
queryset = Project.objects.all()
serializer_class = CountryBasedProjectSerializer

Django DRF add request.user to modelserializer

I am using django rest framework, and I have an object being created via a modelviewset, and a modelserializer. This view is only accessible by authenticated users, and the object should set its 'uploaded_by' field, to be that user.
I've read the docs, and come to the conclusion that this should work
viewset:
class FooViewset(viewsets.ModelViewSet):
permission_classes = [permissions.IsAdminUser]
queryset = Foo.objects.all()
serializer_class = FooSerializer
def get_serializer_context(self):
return {"request": self.request}
serializer:
class FooSerializer(serializers.ModelSerializer):
uploaded_by = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault()
)
class Meta:
model = Foo
fields = "__all__"
However, this results in the following error:
django.db.utils.IntegrityError: NOT NULL constraint failed: bar_foo.uploaded_by_id
Which suggests that "uploaded_by" is not being filled by the serializer.
Based on my understanding of the docs, this should have added the field to the validated data from the serializer, as part of the create method.
Clearly I've misunderstood something!
The problem lies in the read_only attribute on your uploaded_by field:
Read-only fields are included in the API output, but should not be
included in the input during create or update operations. Any
'read_only' fields that are incorrectly included in the serializer
input will be ignored.
Set this to True to ensure that the field is used when serializing a
representation, but is not used when creating or updating an instance
during deserialization.
Source
Basically it's used for showing representation of an object, but is excluded in any update and create-process.
Instead, you can override the create function to store the desired user by manually assigning it.
class FooSerializer(serializers.ModelSerializer):
uploaded_by = serializers.PrimaryKeyRelatedField(read_only=True)
def create(self, validated_data):
foo = Foo.objects.create(
uploaded_by=self.context['request'].user,
**validated_data
)
return foo
DRF tutorial recommend to override perform_create method in this case and then edit serializer so, that it reflect to new field
from rest_framework import generics, serializers
from .models import Post
class PostSerializer(serializers.HyperlinkedModelSerializer):
author = serializers.ReadOnlyField(source='author.username')
class Meta:
model = models.Post
fields = ['title', 'content', 'author']
class ListPost(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
def perform_create(self, serializer):
return serializer.save(author=self.request.user)
Cleaner way:
class PostCreateAPIView(CreateAPIView, GenericAPIView):
queryset = Post.objects.all()
serializer_class = PostCreationSerializer
def perform_create(self, serializer):
return serializer.save(author=self.request.user)
class PostCreationSerializer(serializers.ModelSerializer):
author = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Post
fields = ("content", "author")

Django Rest Framework ViewSets is returning selected records rather than all records in list view

I have a django application. I have one part of my app that is not working as I was expecting. As of now I am using the django rest framework view sets. What I want is two basically have 2 different list views in the same viewset. 1 that returns all of the accounts in the accountviewset and another list view that returns all of the accounts associated with a user based on the user foreignkey in the account model.
The code below returns all the acccounts associated with a specific person, but I am not able to have a list view that returns all of the accounts in the database.
class AccountViewSet(viewsets.ModelViewSet):
serializer_class = AccountSerializer
queryset = Account.objects.all()
lookup_field = 'user__username'
def list(self, request):
queryset = Account.objects.all()
serializer_class = AccountSerializer
def get_object(self):
return self.queryset.get(user__username=self.kwargs.get('username'))
**UPDATE**
class AccountViewSet(viewsets.ModelViewSet):
queryset = Account.objects.all();
serializer_class = AccountSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = ('user', 'name', '_class')
lookup_field = 'user__username'
# def get_queryset(self):
# return self.queryset.filter(user__username=self.kwargs.get('username'))
it works when there is one account associated with one user.
does not work with more than 2 accounts with error:
MultipleObjectsReturned at /api/users/accounts/omarjandali/
get() returned more than one Account -- it returned 2!
other attempts doesnt return any accounts with a specific user or no user in the endpoint.
The biggest problem is that it returns an id of the user not user username.
current code:
serializers.py:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'is_staff')
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('user', 'gender', 'phone', 'ip_address', 'dob_month', 'dob_day',
'dob_year', 'address_street', 'address_city', 'address_state',
'address_postal', 'address_country', 'profile_pic', 'role')
class FriendSerializer(serializers.ModelSerializer):
class Meta:
model = Friend
fields = ('requested', 'friended', 'status', 'blocked')
class AccountSerializer(serializers.ModelSerializer):
user = UserSerializer(
read_only = True,
)
class Meta:
model = Account
fields = ('user', '_id', 'name', 'balance', 'currency', 'bank_name',
'routing', '_class', 'type', 'active', 'main', 'synapse')
Views.py:
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
lookup_field = 'username'
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
queryset = Profile.objects.all()
lookup_field = 'user__username'
class FriendViewSet(viewsets.ModelViewSet):
serializer_class = FriendSerializer
querset = Friend.objects.all()
lookup_field = 'user__username'
class AccountViewSet(viewsets.ModelViewSet):
queryset = Account.objects.all();
serializer_class = AccountSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('user',)
urls:
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
router.register(r'profiles', ProfileViewSet, basename='profile')
router.register(r'friends', FriendViewSet, basename='friend')
router.register(r'accounts', AccountViewSet, basename='account')
urlpatterns = router.urls
First, you have a lot of issues with your code. If you need to return a list, then you should filter in the get_object method. it is meant to only return one single instance, so do that in the get_queryset method. Secondly, you have the get_object() inside your list method which is wrong. Thirdly, assigning the serializer class in the list method doesn't make sense. It is assigned before that method is even called.
Now back to your question, 'list' is a viewset action and you can't implement it twice. Instead you can add an extra action on the list endpoint.
To do this, just add a method in the action method in the viewset.
In your particular case I would do this: I will introduce an instance variable, qs_type which is used to control the type of result returned by the get_queryset() method.
Something like this:
def __init__(self, *args, **kwargs):
self.qs_type = 'user_only'
super().__init__(*args, **kwargs)
def get_queryset(self):
if self.qs_type == 'user_only':
qs = Account.objects.filter(username=self.kwargs.get('username'))
else:
qs = Account.objects.all()
return qs)
#action(detail=False, methods=['get'])
def all(self, request, *args, **kwargs):
self.qs_type = 'all'
return self.list(request, *args, **kwargs)
This assumes that by default, username filtered version is returned while the all accounts will be returned in the new action. You can make qs_type a boolean if you are sure you won't need more similar actions but I made it a string just in case. I don't know how your url is configured so I don't really know how the username is retrieved from the url kwargs, but this code should help you solve your problem

What field in Django Rest Framework allow POST data combining FileField and IntegerField?

I want to write a viewset in Django Rest Framework which allow user upload a list of new Images with these Order number together. But I don't know what field to use in DRF.
My serializer:
class ContentSerializer(ModelSerializer):
fileUpload = FileField(max_length=100000, allow_empty_file=False, use_url=False)
order_num = IntegerField(required=False)
class Meta:
model = Article
fields = [
'fileUpload',
'order_num',
]
class ArticleSerializer(ModelSerializer):
file_uploads = ListField(child=ContentSerializer(),write_only=True)
class Meta:
model = Article
fields = [
'file_uploads',
]
My Viewset:
class FeedEditAPIView(ModelViewSet):
'''
Use Form Data to PATCH
'''
queryset = Article.objects.all()
serializer_class = ArticleSerializer
lookup_field = 'id'
def edit_article(self, request, id):
print request.POST.getlist('file_uploads') // This return [u'{"file":{},"order_num":0}']
When I print request POST file_uploads, it return [u'{"file":{},"order_num":0}']. But I want to return [u'{"file":<files_object>,"order_num":0}'] instead of
Use multipartparser, which supports file uploads and request.data
class FeedEditAPIView(ModelViewSet):
'''
Use Form Data to PATCH
'''
parser_classes = (MultiPartParser,)
queryset = Article.objects.all()
serializer_class = ArticleSerializer
lookup_field = 'id'

Capture parameters in django-rest-framework

suppose this url:
http://localhost:8000/articles/1111/comments/
i'd like to get all comments for a given article (here the 1111).
This is how i capture this url:
url(r'^articles/(?P<uid>[-\w]+)/comments/$', comments_views.CommentList.as_view()),
The related view looks like to:
class CommentList(generics.ListAPIView):
serializer_class = CommentSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
lookup_field = "uid"
def get_queryset(self):
comments = Comment.objects.filter(article= ???)
return comments
For information, the related serializer
class CommentSerializer(serializers.ModelSerializer):
owner = UserSerializer()
class Meta:
model = Comment
fields = ('id', 'content', 'owner', 'created_at')
As you can see, I've updated my get_queryset to filter comments on the article but I don't know how to catch the "uid" parameter.
With an url ending with ?uid=value, i can use self.request.QUERY_PARAMS.get('uid') but in my case, I don't know how to do it.
Any idea?
The url parameter is stored in self.kwargs. lookup_field is the field (defaults to pk) the generic view uses inside the ORM when looking up individual model instances, lookup_url_kwarg is probably the property you want.
So try the following:
class CommentList(generics.ListAPIView):
serializer_class = CommentSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
lookup_url_kwarg = "uid"
def get_queryset(self):
uid = self.kwargs.get(self.lookup_url_kwarg)
comments = Comment.objects.filter(article=uid)
return comments