how to get Json response formatted differently on django - django

I have a custom user model and i implemented a user following and follower system that works well. But the problem I have is that when I request for the followers a particular user has, the JSON response I get is not exactly the way I want it returned. more details below
models
class User(AbstractBaseUser, PermissionsMixin):
username = None
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=250)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=255, unique=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
class FollowLog(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='followers')
followed_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='following', null=True)
followed_on = models.DateTimeField(auto_now_add=True)
status = models.CharField(choices=FOLLOW_STATUS, default=FollowStatus.following.value, max_length=30)
updated_on = models.DateTimeField(auto_now=True)
unfollowed_on = models.DateTimeField(null=True)
blocked_on = models.DateTimeField(null=True)
serializer
class FollowerSerializer(serializers.ModelSerializer):
'''
Allows people to view follower of a user
'''
followed_by = serializers.SlugRelatedField(read_only=True, slug_field='slug')
class Meta:
model = FollowLog
fields = ('followed_by',)
read_only_fields = ('followed_by',)
view
class UserFollowerView(APIView):
'''
Gets all the followers to a user
'''
permission_classes = [AllowAny]
def get(self, request, slug):
user = User.objects.get(slug=slug)
followers = user.followers.all().filter(status='following').order_by("-followed_on")
serializer = FollowerSerializer(followers, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
when I run the above, I get the appropriate response but in this format
JSON response I get
[
{
"followed_by": "opeyemi-odedeyi-qqmunu13o4b8vd4"
},
{
"followed_by": "modupeoluwa-odedeyi-m7ji5qj5szu2uqo"
}
]
I would prefer it if I got a response like below
preferred response I want to get
{
"followed_by": [
"opeyemi-odedeyi-qqmunu13o4b8vd4",
"modupeoluwa-odedeyi-m7ji5qj5szu2uqo"
]
}

A serializer will always return a dictionary, and if you set many=true, it will return an array of dictionaries.
What you can do is to get the array you want from the array of dictionaries returned by the serializer:
class UserFollowerView(APIView):
'''
Gets all the followers to a user
'''
permission_classes = [AllowAny]
def get(self, request, slug):
user = User.objects.get(slug=slug)
followers = user.followers.all().filter(status='following').order_by("-followed_on")
data = FollowerSerializer(followers, many=True).data
data_to_return = list(map(lambda item: item['followed_by'], data))
return Response(data_to_return, status=status.HTTP_200_OK)

You can make a custom renderer for your api and change the format of the response however so you want.
class UserFollowerView(APIView):
'''
Gets all the followers to a user
'''
permission_classes = [AllowAny]
renderer_classes = (UserFollowerRenderer, )
def get(self, request, slug):
user = User.objects.get(slug=slug)
followers = user.followers.all().filter(status='following').order_by("-followed_on")
serializer = FollowerSerializer(followers, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
and the renderer can be something like this :-
class UserFollowerRenderer(CoreJSONRenderer):
charset = "utf-8"
def render(self, data, media_type=None, renderer_context=None):
if not data:
return "[]"
final_data = list()
for element in data:
final_data.append(element.get('followed_by'))
result = dict(followed_by=final_data)
return json_dumps(result)

Related

request.user returning AnonymousUser in ModelViewSet, but working properly in APIView

I am trying to customize get_queryset() in my DocumentViewSet so the GET method will return all Document objects created by request.user (currently logged in user).
However, I am stuck in this error:django.core.exceptions.ValidationError: ['“AnonymousUser” is not a valid UUID.']
I assume this is caused by getting AnonymousUser as my self.request.user. The weird part is that my other APIView that deals with request.user are working flawlessly; The only difference I could find between the two is type of viewset: ModelViewSet vs APIView.
Would appreciate any help!
document.views
class DocumentViewSet(viewsets.ModelViewSet):
model = Document
serializer_class = DocumentSerializer
list_serializer_class = DocumentListSerializer
permission_classes = (AllowAny,)
def get_queryset(self):
user = self.request.user
return Document.objects.filter(user=user)
def get_serializer_class(self):
if self.action == "list":
if hasattr(self, "list_serializer_class"):
return self.list_serializer_class
return super(DocumentViewSet, self).get_serializer_class()
def perform_create(self, serializer):
print(self.request.user)
serializer.save(user=self.request.user)
document.serializers
class DocumentSerializer(serializers.ModelSerializer):
id = HashidSerializerCharField(source_field="documents.Document.id", read_only=True)
question_blocks = QuestionBlockSerializer(many=True)
outline_blocks = OutlineBlockSerializer(many=True)
source_cards = SourceSerializer(many=True, required=False)
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault()
)
class Meta:
model = Document
fields = "__all__"
def create(self, validated_data):
question_blocks = validated_data.pop("question_blocks")
outline_blocks = validated_data.pop("outline_blocks")
source_cards = validated_data.pop("source_cards")
document = Document.objects.create(**validated_data)
for qBlock in question_blocks:
QuestionBlock.objects.create(document=document, **qBlock)
for oBlock in outline_blocks:
OutlineBlock.objects.create(document=document, **oBlock)
for sCard in source_cards:
Source.objects.create(document=document, **sCard)
document.save()
return document
class DocumentListSerializer(serializers.ModelSerializer):
id = HashidSerializerCharField(source_field="documents.Document.id", read_only=True)
class Meta:
model = Document
fields = ("id", "title", "template", "updated")
document.models
class Document(models.Model):
id = HashidAutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
title = models.CharField(max_length=100, default="Untitled")
template = models.CharField(max_length=100, default="")
editorState = models.JSONField(default=[])
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
user.models
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
username = None
first_name = models.CharField(max_length=100, default="unknown")
last_name = models.CharField(max_length=100, default="unknown")
profile_pic = models.CharField(max_length=200, default="unknown")
email = models.EmailField(unique=True, db_index=True)
secret_key = models.CharField(max_length=255, default=get_random_secret_key)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
class Meta:
swappable = "AUTH_USER_MODEL"
users.api & selectors
This is the APIView which returns information of the user currently logged-in; It is working flawlessly.
# users.api
class UserMeApi(APIView):
def get(self, request, *args, **kwargs):
return Response(user_get_me(user=request.user))
# users.selectors
def user_get_me(*, user: User):
return {
"id": user.id,
"name": user.name,
"email": user.email,
"first_name": user.first_name,
"last_name": user.last_name,
"profile_pic": user.profile_pic,
}
EDIT: added document.serializer and rest of the viewset code
My mistake! The origin of the problem was on the frontend; I forgot to include withCredentials:true in axios GET request.
axios
.get<DocumentList>(
`${process.env.NEXT_PUBLIC_API_URL}/drafts/?format=json`,
{
withCredentials: true, //forgot this part!
}
)
.then(function (response) {
setDrafts(response.data.results);
});

Error in the POST method of Writable Nested serializers in django with form data

models.py
class Client(models.Model):
client_id = models.CharField(max_length=50,default=uuid.uuid4, editable=False, unique=True, primary_key=True)
org = models.ForeignKey(Organisation, on_delete=models.CASCADE, related_name='org',null=True)
product = models.ManyToManyField(Product,related_name='product')
client_name = models.CharField(unique=True,max_length=100)
client_code = models.CharField(unique=True,max_length=20)
client_logo = models.ImageField(upload_to=upload_to, null=True, blank=True,)
currency = models.IntegerField(null=True)
currency_type = models.CharField(max_length=100,choices=CURRENCY_CHOICES,default='Indian Rupee')
billing_method = models.ForeignKey(Billing_Method, on_delete=models.CASCADE, related_name='client_billingmethod', null=True)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
email_id = models.EmailField(max_length=100)
contact_no = models.CharField(max_length=20)
class Billing_Method(models.Model):
billing_id = models.CharField(max_length=50, default=uuid.uuid4, editable=False, unique=True, primary_key=True)
billing_name = models.CharField(max_length=50)
description = models.TextField(max_length=250)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
serializers.py
class Billingmethod_Serializers(serializers.ModelSerializer):
class Meta:
model = Billing_Method
fields = ('billing_id', 'billing_name', 'description')
class Clientpost_Serializers(serializers.ModelSerializer):
billing_method = Billingmethod_Serializers()
def create(self, validated_data):
billing_method_data = validated_data.pop('billing_method')
billing_method = Billing_Method.objects.create(**billing_method_data)
client = Client.objects.create(billing_method=billing_method,**validated_data)
return client
class Meta:
model = Client
fields = ('client_id','currency','currency_type','billing_method','first_name','last_name',...)
view.py
class Clientlist(APIView):
renderer_classes = (CustomRenderer,)
parser_classes = [parsers.MultiPartParser, parsers.FormParser]
def get(self, request, format=None):
clients = models.Client.objects.all()
serializer = serializers.Client_Serializers(clients, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = serializers.Clientpost_Serializers(data=request.data)
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)
When i was trying to do POST in client I was getting an errro as
"
TypeError at /api/onboarding/client/
django.db.models.manager.BaseManager._get_queryset_methods..create_method..manager_method() argument after ** must be a mapping, not list
Request Method: POST
Request URL: http://127.0.0.1:8000/api/onboarding/client/
Django Version: 3.2.12
Exception Type: TypeError
Exception Value:
django.db.models.manager.BaseManager._get_queryset_methods..create_method..manager_method() argument after ** must be a mapping, not list
Exception Location: F:\PM-Onboarding-Service\Onboarding-Service\microservices\onboarding\serializers.py, line 34, in create
"
I was doing a Post method in form data as below,
Please help me to solve this error, and let me know how to post the billing method in the form data.
I think you uploaded billing_method as a list in POST API.
But in serializer, you defined it as not a list, but a dictionary.
So you should upload billing _method as an object in postman like the
following.
{
...
"client_code": "...",
"product": "...",
"billing_method": {
"billing_name": "...",
"description": "...",
...
}
}

Want to send extra data from BasePermission to Queryset in ModelVIewSet DRF

Here is My Seller Model which has User as OneToOneField
#models.py
.
.
class CompanyProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="user")
company = models.CharField(max_length=255)
pincode = models.IntegerField()
city = models.ForeignKey(City, on_delete=models.CASCADE)
address = models.TextField()
profileActive = models.BooleanField(default=False)
.
.
Purchase Model
#models.py
class PurchaseOrder(models.Model):
company = models.ForeignKey(CompanyProfile, on_delete=models.CASCADE)
message = models.TextField(blank=True, null=True)
expectedDelivery = models.DateField(blank=True, null=True)
isCancel = models.BooleanField(default=False))
updated_at = models.DateTimeField(auto_now=True)
#Custom permission_classes
class SellerCompanyActive(BasePermission):
message = 'Only Seller with activated company account is allowed'
def has_permission(self, request, view):
user = AuthUser.objects.get(id=request.user.id)
if user.is_seller:
try:
company = CompanyProfile.objects.get(user=user)
if company.profileActive:
return True
else:
self.message = "Company profile is not active"
except CompanyProfile.DoesNotExist:
self.message = "Company not found first create company"
return False
In ModelViewSet
#views.py
class SellerPurchaseOrder(viewsets.ModelViewSet):
queryset = PurchaseOrder.objects.all()
serializer_class = PurchaseOrderSerializer
authorization_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated, SellerCompanyActive]
def get_queryset(self):
user = self.request.user
company = CompanyProfile.objects.get(user=user)
return self.queryset.filter(company=company)
Now here I always had to use this
user = self.request.user
company = CompanyProfile.objects.get(user=user)
As their are lot of other views also, Is their any other way to send data from my custom permission_classes i.e from SellerCompanyActive to direct SellerPurchaseOrder->get_queryset
You can set message attribute to be a dict, something like this:
class SellerCompanyActive(BasePermission):
message = {'error': 'Only Seller with activated company account is allowed'}
def has_permission(self, request, view):
...
you can add more fields to the dictionary to send more data

Django Rest Framework can't parse image data from React front-end

I have a front end where the user can create a 'Post' with or without an image. If the request is done without an image the 'Post' object gets created and there's no problem with it. But when I try to add the image in the post request by using Axios and FormData when Django receives this object my Serializer returns false for is_valid() function call...
An example post object contains the following keys:
{'text':'some string', 'image': {some_file_object(optional)}}
Here's my view:
class PostView(APIView):
permission_classes = (IsAuthenticated,)
parser_class = (MultiPartParser)
def post(self, request):
user = User.objects.get(username=self.request.user)
userSerialize = UserGetSerializer(user, partial=True)
_mutable = request.data.copy()
_mutable['user'] = userSerialize.data
print(_mutable)
serializer = PostSerializer(data=_mutable)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST)
Here's my serializer
class PostSerializer (serializers.ModelSerializer):
text = serializers.CharField(max_length=500, required=True)
user = UserSerializer(many=False)
likes = serializers.SerializerMethodField(read_only=True)
image = serializers.ImageField(required=False)
class Meta:
model = Post
fields = ('__all__')
def get_likes(self, obj):
likes = obj.like_set.filter(post=obj.id)
return LikeSerializer(likes, many=True).data
Here's my Model:
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="user",
on_delete=models.CASCADE, blank=True)
text = models.TextField(max_length=500)
date_posted = models.DateTimeField(auto_now_add=True)
hidden = models.BooleanField(default=False)
date_hidden = models.DateTimeField(blank=True, null=True)
hidden_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True, related_name='hidden_by')
image = VersatileImageField(
upload_to='post_images/', ppoi_field='image_ppoi', blank=True, null=True)
image_ppoi = PPOIField()
def __str__(self) -> str:
return self.text
Make sure you added the right content-type in the header of the request
Refer to the answer to send a file using Axios
How to post a file from a form with Axios

Nested API View of Django REST Framework?

At the moment I developed the following code, for me to get the Contact List of each user. The views return the ID numbers of the Contacts of the User. I need to get, instead of the ID numbers, the 'name' and 'last_name' attribute of said contacts. I am quite new to Django's REST Framework and I'm not quite sure what to do next but I believe I have to nest the APIView. I would really appreciate some help!
views.py
def list_contacts(request, id_profile):
url = request.build_absolute_uri(reverse('api_users:contact_list', kwargs={'pk':id_profile}))
response = requests.get(url)
profile = Profile.objects.get(pk=id_profile)
if response.status_code == status.HTTP_200_OK:
data = response.content
user = json.loads(data)
return render(request, 'profiles/contact_list.html', {'user':user})
models.py
class Profile(models.Model):
id_user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birthday = models.DateField(auto_now=False)
description = models.CharField(max_length=100)
profile_picture = models.ImageField(upload_to='images/profiles/%Y/%m/%d', blank=False)
active = models.BooleanField(default = False)
contacts = models.ManyToManyField('self', blank=True, default='null')
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
deleted_at = models.DateTimeField(blank=True, null=True)
class Meta:
ordering = ('-id',)
def __str__(self):
return self.name+' '+self.last_name
def active_profiles():
return Profile.objects.filter(active=True)
api/views.py
class ContactListView(generics.ListAPIView):
queryset = Profile.objects.all()
serializer_class = UserContactListSerializer
filter_backends = (filters.SearchFilter,)
search_fields = ('name', 'last_name',)
def get(self, request, pk, format=None):
contacts = Profile.objects.get(pk=pk)
serializer = UserContactListSerializer(contacts)
return Response(serializer.data)
api/serializers.py
class UserContactListSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['name','last_name','contacts']
I don't know what exactly is going on in your list_contacts but if you want to use the same serializer as a field in itself, you currently can't.
While Django models allow you to use 'self' as the reference, DRF doesn't.
What you can instead do is create another serializer and use that as the field.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ("id", "first_name", "last_name")
class UserContactListSerializer(serializers.ModelSerializer):
contacts = UserSerializer(many=True)
class Meta:
model = Profile
fields = ("id", "first_name", "last_name", "contacts")