Not to pass ID in URL - UpdateApiView - django

I want to make update using my API. For that i am passing id in URL which is pk.
http://localhost:8000/api/manager/update/96
Where 96 is primary key. Now instead of passing id in url i want to pass id in body and update data. My url should look like this
http://localhost:8000/api/manager/update
Views.py
class ManagerUpdateAPIView(APIView):
def post(self, request, pk, *args, **kwrgs):
user = get_object_or_404(User, id=pk)
userprofile = get_object_or_404(UserProfile, user=pk)
serializer1 = EmployeeRegisterSerializer(user, data=request.data)
serializer2 = EmployeeProfileSerializer(userprofile, data=request.data)
user_role = ACLRoles.objects.get(id=4)
if serializer1.is_valid() and serializer2.is_valid():
serializer1.save()
serializer2.save()
return Response(status=status.HTTP_200_OK)
print(serializer1.errors)
print(serializer2.errors)
return Response(status=status.HTTP_404_NOT_FOUND)
Serializers.py
class EmployeeProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = [
'user_employee_id',
'user_phone',
'user_payroll_id',
'user_hire_date',
'user_pay_rate',
'user_salaried',
'user_excempt',
'user_groups',
'user_state',
'user_city',
'user_zipcode',
'user_status',
]
class EmployeeRegisterSerializer(serializers.ModelSerializer):
# userprofile = EmployeeProfileSerializer(read_only=True)
class Meta:
model = User
fields = ['first_name','last_name', 'email',]
How can i update data without passing id in url.

Try this code
class ManagerUpdateAPIView(APIView):
def post(self, request, *args, **kwrgs): #change is here
pk = request.data['pk'] #change is here
user = get_object_or_404(User, id=pk)
userprofile = get_object_or_404(UserProfile, user=pk)
serializer1 = EmployeeRegisterSerializer(user, data=request.data)
serializer2 = EmployeeProfileSerializer(userprofile, data=request.data)
user_role = ACLRoles.objects.get(id=4)
if serializer1.is_valid() and serializer2.is_valid():
serializer1.save()
serializer2.save()
return Response(status=status.HTTP_200_OK)
print(serializer1.errors)
print(serializer2.errors)
return Response(status=status.HTTP_404_NOT_FOUND)
and provide pk in your POST payload as
{
"pk":96,
"other_data":"other data of your usual payload"
}

Related

Filter Django Queryset to return followers of logged in user only

I am trying work out how filter who follows the logged in user/who is followed by the logged in user using Django Rest Framework. However, all I seem to be returning is ALL users who have been followed by any user or are following any user, it is not specific to the logged in user (the front end view (numbers are the user PK), the API view).
I make a GET Request with the pk of the logged in user via an AJAX call but can't seem to filter specifically against that. I'd be grateful for any help!
Here is my model:
class UserConnections(models.Model):
follower = models.ForeignKey(User, related_name="following", on_delete=models.CASCADE)
followed = models.ForeignKey(User, related_name="followers", on_delete=models.CASCADE)
Here are the relevant serializers:
class UserConnectionListSerializer(serializers.ModelSerializer):
class Meta:
model = UserConnections
fields = ['follower','followed']
class UserConnectionSerializer(serializers.ModelSerializer):
class Meta:
model = UserConnections
fields = '__all__'
depth = 2
Here is the views.py
# gets list of who logged in user is following
class FollowingViewSet(viewsets.ModelViewSet):
serializer_class = serializers.UserConnectionListSerializer
queryset = UserConnections.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return serializers.UserConnectionSerializer
return self.serializer_class
def get_followers(request):
if request.method == "GET":
user_id = self.request.GET.get('current_user_id', None)
return UserConnections.objects.filter(follower__id=user_id)
# gets list of followers who follow logged-in user
class FollowerViewSet(viewsets.ModelViewSet):
serializer_class = serializers.UserConnectionListSerializer
queryset = UserConnections.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return serializers.UserConnectionSerializer
return self.serializer_class
def get_followers(request):
if request.method == "GET":
user_id = self.request.GET.get('current_user_id', None)
return UserConnections.objects.filter(followed__id=user_id)
You need to override get_queryset:
class FollowingViewSet(viewsets.ModelViewSet):
...
# The indentation is in `class` level
def get_queryset(self):
user_id = self.request.GET.get('current_user_id', None)
return UserConnections.objects.filter(follower__id=user_id)
and for FollowerViewSet:
class FollowerViewSet(viewsets.ModelViewSet):
...
def get_queryset(self):
user_id = self.request.GET.get('current_user_id', None)
return UserConnections.objects.filter(followed__id=user_id)

MultipartParser is not validating data in Django Rest Framework

I am using a multipart/form-data in a form which have a manytomany relation as well as multiple file upload. But the validated data doesn't contains the array data
Views.py
class ExpenseCreateAPIView(CreateAPIView):
permission_classes = (permissions.IsAuthenticated,)
parser_classes = ( MultiPartParser,)
def post(self, request, *args, **kwargs):
owner = request.user.pk
d = request.data.copy()
d['owner'] = owner
serializer = ExpenseSerializer(data=d)
print("exp")
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Serializers.py
class ExpenseSerializer(serializers.ModelSerializer):
transactions = ExpenseTransactionsSerializer(many=True, required=False)
bill = ExpenseFilesSerializer(many=True, required=False)
class Meta:
model = Expense
fields = "__all__"
def create(self, validated_data):
print("validated data", validated_data)
items_objects = validated_data.pop('transactions', None)
files_objects = validated_data.pop('bill', None)
prdcts = []
files = []
for item in items_objects:
i = ExpenseTransactions.objects.create(**item)
prdcts.append(i)
if files_objects == None:
pass
else:
for item in files_objects:
i = ExpenseFiles.objects.create(**item)
files.append(i)
instance = Expense.objects.create(**validated_data)
instance.transactions.set(prdcts)
instance.bill.set(files)
return instance
How else should I use the MultiPartParser class in the views ?
I keep getting the error:
TypeError: 'NoneType' object is not iterable
at
for item in items_objects:
Make sure you have transactions in validated_data. If transactions can empty or not required, just change:
validated_data.pop('transactions', None)
to
validated_data.pop('transactions', [])
It means that if transactions not in validated_data then pop [] (an empty list), instead of None, then empty list can keep iterate in next your code.
I think you need to try validated_data.get instead of validated_data.pop

Django Rest Framework serializer is ignoring the model instance im trying to serialize

I´m trying to create an instance of a serializer on a POST request, but it is ignoring the model instance im passing as the first argument
if request.method == 'POST':
if string_pk in reviewed_user_pk:
reviewed_user = User.objects.get(pk=user_pk)
review = Review(author=user, reviewed_user=reviewed_user)
serializer = CreateReviewSerializer(review, data=request.data)
I get user instance from the request:
try:
user = request.user
except user.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
The problem here is that the instance of Review which has both user instances (author and reviewed_user) is being ignored by the ReviewSerializer, here is the serializer:
class CreateReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ['author', 'reviewed_user','title', 'rating', 'comment', 'date_published']
The oter fields in request.data are being serialized but not the Review instance, what can be causing this problem? the error i get from serializer.errors is the following:
{
"author": [
"This field is required."
],
"reviewed_user": [
"This field is required."
]
}
Here is the complete function view:
#api_view(['POST'])
#permission_classes((IsAuthenticated,))
def api_create_review_view(request, user_pk): #user_pk is the pk of the reviewed_user
try:
user = request.user
except user.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
reviewed_user_pk = user.worked_with.split(',')
string_pk = str(user_pk)
data = {
}
if request.method == 'POST':
if string_pk in reviewed_user_pk:
reviewed_user = User.objects.get(pk=user_pk)
review = Review(author=user, reviewed_user=reviewed_user)
serializer = ReviewSerializer(review, data=request.data)
if serializer.is_valid():
serializer.save()
reviews_count = reviewed_user.reviews_count
rating = ((reviewed_user.rating * reviews_count) / (reviews_count + 1)) + ((serializer.rating) / (reviews_count + 1))
reviews_count += 1
reviewed_user.rating = rating
reviewed_user.reviews_count = reviews_count
reviewed_user.save()
return Response(serializer.data)
data = serializer.errors
return Response(data)
else:
data = {
'forbidden':'users have not worked together'
}
return Response(data=data)
And here is the Review model:
class Review(models.Model):
reviewed_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='reviewed_user')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='author')
rating = models.IntegerField(default=5)
title = models.CharField(max_length=50)
comment = models.CharField(max_length=500)
date_published = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Right now im not worried that the code inside the if statement that checks if the serializer is valid works, i just want to know how can i pass the instance of Review with both user instances inside to the serializer so the serializer is valid
if you are try to create a new instance of Review. Try this two method, i am not sure which one can work.
first way, change the request.data:
from copy import deepcopy
reviewed_user = User.objects.get(pk=user_pk)
serializer_data = deepcopy(request.data)
serializer_data['user'] = user
serializer_data['reviewed_user'] = reviewed_user
serializer = ReviewSerializer(serializer_data)
if serializer.is_valid():
serializer.save()
second way,just change serializer to partial with set partial=partial in Serializer:
reviewed_user = User.objects.get(pk=user_pk)
review = Review(author=user, reviewed_user=reviewed_user)
serializer = ReviewSerializer(review, data=request.data, partial=partial)
if serializer.is_valid():
third way, not validate user and reviewed_user in serializer, just save it,
remove 'author', 'reviewed_user' in serializer:
class CreateReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ['title', 'rating', 'comment', 'date_published']
reviewed_user = User.objects.get(pk=user_pk)
serializer = ReviewSerializer(data=request.data)
if serializer.is_valid():
serializer.save(author=user, reviewed_user=reviewed_user) # save user and reviewed_user here

How do I correctly retrieve objects from a database using Django REST api?

I'm teaching my self how to use the Django REST api but I'm running into a JSONDecodeError. I'm trying to display posts depending on the user that is stored in the session. I'm storing the user username, password, and id in the session for later use.
The JSONDecodeError is targeting the line with instruction posts = posts_response.json() when I look at the HTML error page
Here's my views.py:
posts_path = "http://127.0.0.1:8000/posts/"
def index(request):
if request.method=='GET':
post_form = PostForm()
posts_response = requests.get(posts_path)
posts = posts_response.json()
context = {
'post_form': post_form,
'post_list': posts,
}
return render(request, 'UserLanding/index.html', context)
else:
return render(request, "UserLanding/index.html")
This is my viewsets:
class PostView(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
def get_queryset(self):
query_set = self.queryset.filter(user=self.request.session['id'])
return query_set
class SubscriberView(viewsets.ModelViewSet):
queryset = Subscriber.objects.all()
serializer_class = SubscriberSerializer
My registered urls:
router = routers.DefaultRouter()
router.register('posts', viewsets.PostView)
router.register('users', viewsets.SubscriberView)
And serializers:
class SubscriberSerializer(serializers.ModelSerializer):
class Meta:
model = Subscriber
fields = ('id', 'username', 'password')
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('id', 'post_text', 'pub_date', 'user')
Please help me spot the problem. Thanks in advance
EDIT:
I was able to fix the problem by adding a function to my PostView code like this:
#action(detail=False, methods=['GET'], url_path=r'(?P<user_id>[0-9]+)')
def find_by_user_id(self, request, user_id):
user = Subscriber.objects.get(id=user_id)
posts = Post.objects.filter(user=user)
serializer = self.serializer_class(posts, many=True)
return Response(serializer.data)
and my request.get() now points to this:
posts_response = requests.get(posts_path + str(request.session['id']))

Getting and saving value from POST with nested serializer

class InfoSerializer(serializers.ModelSerializer):
class Meta:
model = EventInfo
fields = ('email', 'pin')
class EventSerializer(DataSerializer, GeoModelAPIView):
# other fields
event_info = InfoSerializer(read_only=True)
def create(self, validated_data):
event_info = validated_data.pop('event_info', {})
event = super().create(validated_data)
EventInfo.objects.create(event=event, **event_info)
return event
Model
class EventInfo(models.Model):
pin = models.CharField(max_length=60, null=False, blank=False)
email = models.EmailField()
event = models.ForeignKey(Event)
POST
{
# other data
"event_info": {
"email": "example#example.com",
"pin": "1234567890"
}
}
So I have a model that is not visible on the browsable API, but I want to be able to save data from POST request to that model. Using this code I can create the objects and it correctly links the info to a correct Event model. However the email and pin fields won't get saved. What I have figured out is that the 'event_info' data from the POST is not visible on the validated_data.
The validation goes to the DataSerializer's validation method but I guess that I should somehow bypass the validation for just the 'event_info' data?
Edit:
class EventViewSet(BulkModelViewSet, JSONAPIViewSet):
queryset = Event.objects.filter(deleted=False)
queryset = queryset.select_related('location')
queryset = queryset.prefetch_related(list of related fields)
serializer_class = EventSerializer
filter_backends = (EventOrderingFilter, filters.DjangoFilterBackend)
filter_class = EventFilter
ordering_fields = (fields to order by)
ordering = ('-last_modified_time',)
def __init__(self, **kwargs):
super().__init__(**kwargs)
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
def get_serializer_context(self):
context = super(EventViewSet, self).get_serializer_context()
context.setdefault('skip_fields', set()).update(set([
'headline',
'secondary_headline']))
return context
#atomic
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
def perform_create(self, serializer):
if isinstance(serializer.validated_data, list):
event_data_list = serializer.validated_data
else:
event_data_list = [serializer.validated_data]
super().perform_create(serializer)