'ArticleSerializer' object has no attribute 'get' - django

The article serializer:
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model=Article
fields='__all__'
settings:
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
model:
class Article(models.Model):
title = models.CharField(max_length=50)
author = models.CharField(max_length=50)
email=models.EmailField()
date=models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
view function:
def article_list(request):
if request.method== 'GET':
article =Article.objects.all()
serializer = ArticleSerializer(article,many=True)
return JsonResponse(serializer.data,safe=False)
if request.method == 'POST':
data = JSONParser.parse(request)
serializer = ArticleSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data,status=201)
return JsonResponse(serializer.errors,status=400)
i just created a simple serializer but whenever i try to get the response it says that the ArticleSerializer i created has no attribute 'get'.
Error

Pass request.data to the serializer. You are parsing the whole request object and passing that instead of the required data.
...
if request.method == 'POST':
serializer = ArticleSerializer(data=request.data)

Related

How to serialize an already existing image to save in a models.ImageField param?

I want to create Note object which one of the fields of the model is an ImageField using Django Rest Framework.
I can already create objects and update all different fields using my API, except for images.
My code:
models.py
class Note(OwnedModel):
note_id = models.UUIDField(primary_key=True,
default=uuid.uuid4,
editable=False)
# note_owner = models.ForeignKey(, null=True, blank=True, on_delete=models.SET_NULL)
note_name = models.CharField(max_length=50)
body = models.TextField()
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
qr_image = models.ImageField(upload_to='notes', null=True)
def __str__(self):
return self.note_name[0:50]
class Meta:
ordering = ['-updated']
views.py
#api_view(['GET', 'POST'])
def getNote(request, pk=None):
if request.method == 'GET':
note = Note.objects.get(note_id=pk)
serializer = NoteSerializer(note, many=False)
return Response(serializer.data)
elif request.method == 'POST':
_data = request.data.copy()
owner = request.user.id
_data["owner"] = owner
# Generate QR code
qr_image = generate_qr(_data["note_name"])
# HOW TO PASS THE IMAGE TO THE SERIALIZER?
_data["qr_image"] = qr_image
# _data["qr_image"] = qr_image[0]
# _data["qr_image"] = qr_image[1]
serializer = NoteSerializer(data=_data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST)
serializers.py
class NoteSerializer(ModelSerializer):
class Meta:
model = Note
fields = '__all__'
qr_code.py
import qrcode
def generate_qr(qr_file_name=None):
qr = qrcode.QRCode(
version=1,
# error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=4,
border=3,
)
qr.add_data(qr_file_name)
qr.make(fit=True)
img = qr.make_image()
# img = qr.make_image(fill_color="black", back_color="white")
path='images/notes/'+str(qr_file_name)+'.jpg'
img.save(path)
return path, img
The QR code is properly generated and saved in the upload path perfectly.
What I cannot manage to build line _data["qr_image"] = qr_image correctly, or if I need to return the image in a different way from the generate_qr function. Everything else it is working well (for example create the Note object form the Admin and uploading the qr image).
#api_view(['GET', 'POST'])
def getNote(request, pk=None):
if request.method == 'GET':
# automatically raise 404 if obj does not exist
note = get_object_or_404(Note.objects, note_id=pk)
# many=False is default, dont need that
serializer = NoteSerializer(note)
return Response(serializer.data)
elif request.method == 'POST':
serializer = NoteSerializer(data=request.data)
# raise_exception=True will raise validation error (400) automatically
serializer.is_valid(raise_exception=True)
# get note name after validation to be sure it has proper length etc.
qr_image = generate_qr(serializer.validated_data.get("note_name"))
# attributes provided here will bypass validation
# and will be injected directly to model create method
serializer.save(
owner=request.user,
qr_image=qr_image
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
Im not sure that your generate_qr function should save image in the given path because Note should do it for you on create.
Tip: check the viewsets.GenericViewSet class - it will properly split your view into methods and makes the life easier.

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)

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

Inconsistent file paths in django rest framework

I have the following view:
class UserProfileView(APIView):
permissions_classes = [permissions.IsAuthenticated]
def get(self, request):
user = User.objects.get(id=request.user.id)
serializer = UserPrivateSerializer(user)
return Response(serializer.data)
The following Model:
class User(AbstractUser):
pp = models.ImageField(blank=True)
and the following serializer:
class UserPrivateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
The following urls:
urlpatterns = [
path('profile/', UserProfileView.as_view())
]
I response I get is:
{
"pp": "/media/WIN_20190423_18_50_32_Pro.jpg"
}
when I expect:
{
"pp": "localhost:8000/media/WIN_20190423_18_50_32_Pro.jpg"
}
I know it's not a model or serializer issue because I have other views that use the same model and serializer where it returns the full path.
try this:
class UserProfileView(APIView):
permissions_classes = [permissions.IsAuthenticated]
def get(self, request):
user = User.objects.get(id=request.user.id)
serializer = UserPrivateSerializer(user, context=self.get_serializer_context())
return Response(serializer.data)
the key is add context=self.get_serializer_context() to you serializer.
It turns out all I do was to add context={"request": request} to the serializer.

django rest framework - POST request causes 400 status code

I am trying to perform a POST request to create an article and I am getting this error Request failed with status code 400 Bad Request: /api/articles/create/.
An article needs 3 attributes to be created:
(1) title
(2) body
(3) author (the current user)
The router works fine since the POST request goes into the post method of the ArticleCreateView class. But I'm guessing that serializer.is_valid() is returning False for some reason.
Also print(request.data) returns {'title': 'hello', 'body': 'stuff'}
Another mystery about Django is, how does the serializer know if I want to get, create or update something? In all the examples I've seen, the serializer magically seems to know this.
class ArticleCreateView(CreateAPIView):
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
def create(self, validated_data):
author = self.context['request'].user
title = validated_data.get('title')
body = validated_data.get('body')
return Article.objects.create(author=author, title=title, body=body)
class Article(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
body = models.TextField(max_length=100)
date_updated = models.DateTimeField(auto_now=True)
date_created = models.DateTimeField(auto_now_add=True)
According to your serializer, the validation process needs author details, which is not passing through POST payload.
So, make the author field in the serializer a not required field by adding required=False or use a ReadOnlyField() or specify read_only_fields in Meta class. Also pass context data as #nishant mentioned
# views.py
class ArticleCreateView(CreateAPIView):
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = ArticleSerializer(data=request.data, context={"request": request}) # change here
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
# serializer.py
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('author',)
def create(self, validated_data):
author = self.context['request'].user
title = validated_data.get('title')
body = validated_data.get('body')
return Article.objects.create(author=author, title=title, body=body)
add this
serializer = ArticleSerializer(data=request.data, context={'request':request})