How to update a user profile field using rest API - python

I am new to django and very confused. I am using django as the backend API for my angular application.
I want to add few more details to the User model so I added the following to my models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
company_name = models.CharField(max_length=100)
I am using an application to add rest authentication support: https://github.com/Tivix/django-rest-auth
Using this application I can edit a users profile with this URL doing a POST request: http://localhost:8080/rest-auth/user/
Question
How can I update the custom field company_name? while editing a users profile?
What I've tried
I tried to override the UserDetailsSerializer that the application provides but it isn't having any effect.
This is what I tried adding to my applications serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserProfile
fields = ('company_name',)
class UserDetailsSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer(required=True)
class Meta:
model = models.User
fields = ('id', 'username', 'first_name', 'last_name', 'profile')

If you are using django rest framework, then you basically want to have an update method on your view class for UserProfile that takes in the user id as a request param. Then in that method you want to use the ORM to get a model object for the given userprofile id, set the property that was also passed as a param, and save the changed model object. Then generate a success response and return it.
You can read more about how to do this here: http://www.django-rest-framework.org/api-guide/views/

Related

Object has no attribute get in serializer

I created a serializer and an API endpoint so I can retrieve some data from a Django DB in my React app but getting this error message:
AttributeError: 'ProgrammingChallengesView' object has no attribute 'get'
Here is my models.py:
#creating programming challenges
class ProgrammingChallenges(models.Model):
challenge_id = models.AutoField(primary_key=True)
challenge_name = models.CharField(max_length=200)
challenge_description = models.TextField()
challenge_expectations = models.TextField()
my serializer:
from accounts.models import ProgrammingChallenges
...
class ProgrammingChallengesView(serializers.ModelSerializer):
class Meta:
model = ProgrammingChallenges
fields = '__all__'
and my urls.py:
path('api/programming_challenges/', ProgrammingChallengesView, name='programming_challenges'),
Thanks to the comments; I clearly didn't understand that a serializer only transforms my data to make it available through an API. I still had to create a view for my API's endpoint.
I opted to create a ReadOnlyModelView because I only want to GET data from this endpoint.
Here is what I wrote in my views:
class ProgrammingChallengesView(ReadOnlyModelViewSet):
serializer_class = ProgrammingChallengesSerializer
queryset = ProgrammingChallenges.objects.all()
#action(detail=False)
def get_list(self, request):
pass
and in my urls.py:
path('api/programming_challenges/', ProgrammingChallengesView.as_view({'get':'list'}), name='programming_challenges'),
I think you shouldn't hurry read the docs again. You are trying to use serializers as views.
Models - are representation of db tables as class.
Serializer serializes the data to json.
View accepts the reqeust from client and returns a Response.
Code shoudld be:
models.py
class ProgrammingChallenge(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
expectations = models.TextField()
Your model name should be ProgrammingChallenge(singular) not ProgrammingChallenges(plural).
You should't add prefix challenge before all field names. Because we already know that the fields are in a Model called ProgrammingChallenge. And it is easy to access them like ProgrammingChallenge.name than ProgrammingChallenge.challenge_name
You don't have to add field id manually. Django model automatically adds id field as primary_key
serializer.py
from accounts.models import ProgrammingChallenge
...
class ProgrammingChallengeSerializer(serializers.ModelSerializer):
class Meta:
model = ProgrammingChallenge
fields = '__all__'
No problem in serialize.
Now, main problem is you don't have any view. You definetly read docs. You can use APIView, generic views or viewset. In this example i'm going to use ViewSet that handles CRUD operations built in.
viewsets.py
from rest_framework.viewsets import ModelViewSet
from .models import ProgrammingChallenge
from .serializers import ProgrammingChallengSerializer
class ProgrammingChallengViewSet(ModelViewSet):
queryset = ProgrammingChallenge.objects.all()
serializer_class = ProgrammingChallengeSerializer
urls.py
from rest_framework.routers import SimpleRouter
from .viewsets import ProgrammingChallenge
router = SimpleRouter()
router.register('challengr', ProgrammingChallengeViewSet)
urlpatterns = router.urls
Another advantage of using viewset, it also generate all endpoint for it's CRUD methods automatically via routes.
It should help you to start your first project.
AGAIN, READ THE DOCS!

Django Rest Framework PUT request to user model endpoint

I'm trying to update my user model by adding a profile picture. I'm stuck on the fact that DRF requires a lookup for PUT requests. But the client doesn't know the user pk they just have a login token. The user would normally be obtained in a view using request.user
views.py:
class UploadViewset(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = serializers.ImageUploadSerializer
parser_classes = [MultiPartParser]
models.py
class User(AbstractUser):
profile_image = models.ImageField(upload_to='testuploads/', null=True)
serializers.py
class ImageUploadSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'profile_image',
)
Posting give the following
"detail": "Method \"PUT\" not allowed.",
Which is caused by the fact that there is no individual resource identifier in the URL. Of course, the resource (a user) could be obtained from the request because it is an authenticated request.
How can I update the user model using DRF?
Update
urls.py
router.register(r'upload', views.UploadViewset)
I'm posting to endpoin upload/

Django REST POST with PK to HyperlinkedModelSerializer, how to translate PK to URL?

I'm new to Django and Django REST. I have been creating a simple practice project.
Models:
class Locker(models.Model):
locker_owner = models.ForeignKey(User, related_name='lockers', on_delete=models.CASCADE)
locker_desc = models.CharField(max_length=100)
locker_rating = models.IntegerField(default=0)
Serializers:
class LockerSerializer(serializers.ModelSerializer):
locker_owner = serializers.HyperlinkedRelatedField(
view_name='user-detail',
lookup_field='id',
queryset=User.objects.all())
# how to add locker owner with id?
class Meta:
model = Locker
fields = ('url', 'locker_desc', 'locker_rating', 'locker_owner')
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'id', 'username', 'email', 'groups')
Views:
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
class LockerViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows lockers to be viewed or edited.
"""
# def create(self, request):
queryset = Locker.objects.all()
serializer_class = LockerSerializer
I use this to POST:
ttp -f POST localhost:8000/lockers/ locker_owner_id=2 locker_desc='desc of locker 3' locker_rating=150
But then the response is
"locker_owner": [
"This field is required."
]
Main Point:
I would like to create a new locker, and use User id as its locker_owner, yet still maintain the HyperlinkedModelSerializer. It won't let me, since they need url for locker_owner. Once I use hyperlink in the locker_owner of my POST request it works, but I don't know how to translate locker_owner=2 to it's url.
Any help would be appreciated. I have been looking for answers in days. Thank you!
This is not something you can do out-of-the-box. You will need to implement your own custom serializer Field, and override the to_representation method:
serializers.py
from rest_framework.reverse import reverse
class CustomRelatedField(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
return reverse('<your-user-detail-view-name>', args=(value.pk,), request=self.context['request'])
class LockerSerializer(serializers.ModelSerializer):
locker_owner = CustomRelatedField(queryset=User.objects.all())
class Meta:
model = Locker
fields = ('url', 'locker_desc', 'locker_rating', 'locker_owner')
You can then simply POST a simple JSON to create a new Locker object:
{
"locker_owner": 2,
"locker_desc": 'desc of locker 3',
"locker_rating": 150
}
I have found the solution myself looking at the tutorials available. The other answers have not really answered the question fully.
For those wanting to get url of a foreign key to be saved, here, locker_owner, just get them using hyperlinked related field
In my serializer:
class LockerSerializer(serializers.HyperlinkedModelSerializer):
locker_owner=serializers.HyperlinkedRelatedField(
read_only=True,
view_name='user-detail')
class Meta:
model = Locker
fields = ('locker_desc', 'locker_rating', 'locker_owner')
I was trying to get the id in the lookup field, but actually the lookup field is searching the id from my url. That's why it could not find it. Simple and done. Thank you for all the answers.

How to add and serialise additional fields of an existing model in Django

I'm using django-rest-framework and python-social-auth in my Django project.
Here is the serializer class of UserSocialAuth model in my project
class SocialAuthSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.CharField()
class Meta:
model = UserSocialAuth
fields = ('id', 'provider')
Then I want to an additional field UserSocialAuth.extra_data['login'] to above Serializer, the traditional way should be
class UserSocialAuth(AbstractUserSocialAuth):
def login:
return self.extra_data['login']
class SocialAuthSerializer(serializers.HyperlinkedModelSerializer):
login = serializers.CharField(source='login')
...
fields = ('id', 'provider', 'login')
The problem is that UserSocialAuth is belong to python-social-auth, I have to change the code of python-social-auth app directly to add def login:, so how can I add the additional field to the existing model UserSocialAuth without touching the code of python-social-auth.
I just find that I can use SerializerMethodField here, no need to change the raw class UserSocialAuth, just add one more field to the serializer like this:
class SocialAuthSerializer(serializers.HyperlinkedModelSerializer):
login = serializers.SerializerMethodField()
def get_login(self, obj):
return obj.extra_data['login']

How to serialize the return value of a method in a model?

I have a Django app that uses Django REST Framework and Django-allauth. Now, I'm trying to retrieve the avatar URL that's present in the social account. However, I'm having trouble forming the serializer.
For background info, the allauth's SocialAccount is related to the user model via a foreign key:
class SocialAccount(models.Model):
user = models.ForeignKey(allauth.app_settings.USER_MODEL)
# the method I'm interested of
def get_avatar_url(self):
...
Now, here's the serializer I currently have:
class ProfileInfoSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name', 'last_name')
In the end, I'd like to have an extra field avatar_url which takes the result of the method call in the SocialAccount model. How do I do that?
I found a solution using a SerializerMethodField:
class ProfileInfoSerializer(serializers.ModelSerializer):
avatar_url = serializers.SerializerMethodField()
def get_avatar_url(obj, self):
return obj.socialaccount_set.first().get_avatar_id()
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'avatar_url')

Categories