I'm new to Django and Python, I'm using Django-rest-framework for building RESTfull API.
I have a view like this
class ProfileViewSet(APIView):
# to find if isAuthenticate then
authentication_classes = (TokenAuthentication,)
permission_classes = [permissions.IsAuthenticated]
def post(self, request):
user_id = request.data['user_id']
request.data.pop('user_id')
request.data['user_id'] = int(user_id)
serializer = ProfileSerializer(
context={'request': request}, data=request.data)
if serializer.is_valid():
serializer.create(validated_data=request.data)
return Response(serializer.data)
return Response(serializer.errors)
and my serializer goes something like this
class ProfileSerializer(serializers.ModelSerializer):
# id = serializers.IntegerField(source='profile.id')
user_id = serializers.IntegerField(source='user.id')
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault())
profile = Profile
# depth=2
class Meta:
model = Profile
fields = ('id', 'user', 'image', 'first_name', 'last_name',
'description', 'mobile', 'current_location', 'user_id')
read_only_fields = ('user', 'user_id')
from my frontend, I'm sending user_id as a string so I'm parsing it to number.
error:- TypeError: Field 'id' expected a number but got [28].
JSON from request.data:- 'user_id':'28'
'first_name':'sdsd'
'last_name':'dsd'
'mobile':'2323323'
'current_location':'23233'
description':'23'
in JSON, '28' is different from 28 just like a programming language, cause '28' is a string, not a number
Related
I'm trying to set endpoints to PATCH or DELETE users according to a permission but my current code only allows me to apply those changes to the account I'm logged to. Is there a way for me to pass an id or an email as an argument in the body of a request to modify other accounts without using the ID in the URL? Thank you.
serializers.py
class UserModifySerializer(serializers.ModelSerializer):
class Meta:
model = User
exclude = ['id', 'user_role']
class UserSerializer(serializers.ModelSerializer):
organization = OrganizationSerializer(read_only=True)
class Meta:
model = User
fields = ['id', 'email', 'first_name', 'last_name', 'organization', 'user_role', 'language']
models.py
class UserViewSet(mixins.UpdateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
swagger_schema = None
serializer_class = UserSerializer
#action(detail=False, methods=['patch'], permission_classes = [IsAuthenticated], url_path='modify-user')
def update_user(self, request):
user = User.objects.get(id=self.request.user.id)
if user.user_role in [Roles.ORGANIZATION_USER, Roles.ORGANIZATION_ADMIN, Roles.WEBSITE_ADMIN]:
serializer = UserModifySerializer(user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save(**serializer.validated_data)
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_401_UNAUTHORIZED)
#action(detail=False, methods=['delete'], permission_classes = [IsWebsiteAdmin, IsOrganizationAdmin])
def delete_member(self, request):
user = User.objects.get(id=self.request.user.id)
if user.user_role in [Roles.ORGANIZATION_ADMIN, Roles.WEBSITE_ADMIN]:
members = User.objects.filter(organization=self.request.user.organization)\
.filter(id=id).delete()
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_401_UNAUTHORIZED)
You can put the necessary data in a json format inside request's body and then access it in the view like so:
data = json.loads(request.body)
How you put the data depends on how you call the view.
I am writing an api for Auth. and I have 2 user types ( Donors , Charity ) when they did registration my data is changing. So that I need to update my fields according to the data.
Here is what I did so far.
views.py
class RegisterView(APIView):
def post(self, request):
js_data = request.data
if 'last_name' in js_data:
#fields = ['id', 'email', 'first_name', 'last_name', 'password']
serializer = UserSerializer(data=js_data)
serializer.is_valid(raise_exception=True)
serializer.save()
else:
#fields = ['id', 'email', 'first_name', 'postcode', 'password']
serializer = UserSerializer(data=js_data)
serializer.is_valid(raise_exception=True)
serializer.save()
in views.py I tried to define fields from outside serializers.py but I can't send fields as a parameter.
serializer = UserSerializer(data=js_data, fields = fields) # gives error.
also I tried this one;
serializer = UserSerializer(data=js_data, many= True, fields = fields) # gives error
So I am trying to create fields dynamically.
serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = Users
fields = []
extra_kwargs = {'password': {'write_only': True} }
def create(self, validated_data):
if 'last_name' in validated_data:
self.Meta.fields = ['id', 'email', 'first_name', 'last_name', 'password']
else:
self.Meta.fields = ['id', 'email', 'first_name', 'postcode', 'password']
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
So in here I tried to change fields in create function. I am not getting an error but in database my fields are empty :( Maybe I define fields = [] as a empty list thats why fields are empty and not saving any data of user.
Btw, I started to learn django rest. I new at this framework so that I am sorry to any false definition or etc..
So any ideas to create fields according the data? Thanks in advance. Have a nice day.
The best solution would be to have 2 separate serializers:
Make 2 different serializers, each with its own logic and fields
If some logic is shared between both serializers, simply create a parent class with the shared logic, and make them inherit from it
In your view, simply pick the serializer based on your need, like so:
if 'last_name' in js_data:
serializer = SerializerOne(data=js_data)
else:
serializer = SerializerTwo(data=js_data)
serializer.is_valid(raise_exception=True)
serializer.save()
Having 2 separate serializer will make the code cleaner and easier to understand and maintain
I am trying to implement multiple user types in DRF and I'm doing that by having a
User Model - which has login related fields and common fields in all the roles and also a choice field denoting type of user.
Customer Model - OneToOneField with user model.
Seller Model - OneToOneField with user model.
I have set up authentication and permissions for User model and now I'm able to log in. I want the logged in user to be able to create his respective profile (based on user_type field of User model).
class User(AbstractBaseUser, PermissionsMixin):
""" A Generic User inside our system. The fields used are common to all users in system. """
....
class Customer(models.Model):
"""A Class to represent a Customer in System """
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
Now I am trying to figure out how to allow a user to create a profile respective to his user_type (customer/seller). and the more confusing part is how do I set the user to current logged in user for my CustomerSerializer or SellerSerializer
This is the permission class I'm trying to use:
class UpdateCustomerProfile(permissions.BasePermission):
"""Allow users to edit their own profile """
def has_object_permission(self, request, view, obj):
"""Check if user is trying to edit their own profile"""
return obj.user.id == request.user.id
and this is the customer serializer:
class CustomerSerializer(serializers.ModelSerializer):
"""A Serizlier class for customer """
class Meta:
model = models.Customer
fields = ('user', 'first_name', 'last_name', 'dob', 'gender')
def create(self, validated_data):
"""Create and return a new customer."""
CustomerViewSet:
class CustomerViewSet(viewsets.ModelViewSet):
"""Handle creating reading and updating Users in system"""
serializer_class = serializers.CustomerSerializer
queryset = models.User.objects.filter( user_type = "CS" )
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.UpdateCustomerProfile,)
But I get an error
AttributeError at /api/customer-profile/
Got AttributeError when attempting to get a value for field user on serializer CustomerSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'user'.
I'm new to Django so I'm not sure If this is a way to do it or if I'm doing anything wrong. How can I fix this? Any examples projects following similar strategy would also be very helpful.
Since your serializer is for a Customer, your queryset should be for a Customer:
queryset = models.Customer.objects.filter(user=request.user)
for example, if you only want to the Customer profile of the current user.
class CustomerViewSet(viewsets.ModelViewSet):
"""Handle creating reading and updating Users in system"""
serializer_class = serializers.CustomerSerializer
#you are using customer model for serializer but for query set you are using
#User model.
queryset = models.Customer.objects.filter( user__type = "CS" )
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.UpdateCustomerProfile,)
Hey got my code below to work for serializing registration with multiple user types. I followed this: https://www.freecodecamp.org/news/nested-relationships-in-serializers-for-onetoone-fields-in-django-rest-framework-bdb4720d81e6/
here is my models.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
is_individual = models.BooleanField(default=False)
is_company = models.BooleanField(default=False)
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
company_name = models.CharField(max_length=100)
email_address = models.EmailField(max_length=254, blank=True, null=True)
class Individual(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
email_address = models.EmailField(max_length=254)
here is my serializers.py:
from rest_framework import serializers
from classroom.models import User, Individual, Company
from django.contrib.auth import authenticate
class IndividualSerializer(serializers.ModelSerializer):
class Meta:
model = Individual
fields = ('user', 'email_address')
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('user', 'email_address', 'company_name')
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'password', 'is_individual', 'is_company')
extra_kwargs = {'password': {'write_only': True}}
class IndividualRegisterSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = Individual
fields = ('user', 'email_address')
extra_kwargs = {'password': {'write_only': True}, 'username': {'write_only': True}}
def create(self, validated_data, *args, **kwargs):
user = User.objects.create_user(validated_data['user']['username'], validated_data['email_address'], validated_data['user']['password'])
individual = Individual.objects.create(user=user, email_address=validated_data.pop('email_address'))
return individual
class CompanyRegisterSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = Company
fields = ('user', 'company_name', 'email_address')
extra_kwargs = {'password': {'write_only': True}, 'username': {'write_only': True}}
def create(self, validated_data, *args, **kwargs):
user = User.objects.create_user(validated_data['user']['username'], validated_data['email_address'],
validated_data['user']['password'])
company = Company.objects.create(user=user, email_address=validated_data.pop('email_address'), company_name=validated_data.pop('company_name'))
return company
class IndividualLoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
individual = authenticate(**data)
if individual and individual.is_active:
return individual
raise serializers.ValidationError("Incorrect Credentials")
class Meta:
fields = ['username','password','is_individual','is_company']
extra_kwargs = {'is_individual': {'required': False},
'is_company': {'required': False}}
and here is my api.py:
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import generics, permissions
from knox.models import AuthToken
from ..serializers import \
UserSerializer, \
IndividualRegisterSerializer, CompanyRegisterSerializer, \
class RegisterIndividualAPI(generics.GenericAPIView):
serializer_class = IndividualRegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
individual = serializer.save()
individual_data = IndividualSerializer(individual, context=self.get_serializer_context()).data
return Response({
"individual": individual_data,
"username": individual.user.username,
"token": AuthToken.objects.create(individual.user)[1]
})
class RegisterCompanyAPI(generics.GenericAPIView):
serializer_class = CompanyRegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
company = serializer.save()
company_data = CompanySerializer(company, context=self.get_serializer_context()).data
return Response({
"company": company_data,
"username": company.user.username,
"token": AuthToken.objects.create(company.user)[1]
})
class LoginCompanyAPI(generics.GenericAPIView):
serializer_class = CompanyLoginSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
company = serializer.validated_data
company_data = CompanySerializer(company, context=self.get_serializer_context()).data
return Response({
"company": company_data,
"username": company.user.username,
"token": AuthToken.objects.create(company.user)[1]
})
And here is my API POST request's body:
{
"user": {
"username":"nind5",
"password": "123456",
"is_individual" : "True",
"is_company" : "False"
},
"email_address":"n#gmail.com"
}
I also used this resource https://www.youtube.com/watch?v=0d7cIfiydAc&t=437s. Knox is explained in this video as well.
Also, use this online tool to view your db (db.sqlite3) tables : https://sqliteonline.com/
Best,
Nick
I Implemented a class based view in the views.py though when I tried to update an employee I realized that it's like I'm trying to create new one yet I have the PUT method defined. I have have an issue updating the user details since user field is a Foreign key.
A user with that username already exists.
Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.
views.py
class EmployeeDetailView(APIView):
permission_classes = [AllowAny]
# queryset = Employee.objects.all()
# serializer_class = EmployeeSerializer
"""
Retrieve, update or delete a employee instance.
"""
def get_object(self, pk):
try:
return Employee.objects.get(pk=pk)
except Employee.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
employee = self.get_object(pk)
serializer = EmployeeSerializer(employee)
return Response(serializer.data)
def put(self, request, pk, format=None):
employee = self.get_object(pk)
serializer = EmployeeSerializer(employee, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
class EmployeeSerializer(serializers.ModelSerializer):
user = UserSerializer()
contract_type = ContractSerializer(read_only=True)
company = CompanySerializer(read_only=True)
job_title = JobSerializer(read_only=True)
department = DepartmentSerializer(read_only=True)
skill = SkillSerializer(read_only=True)
unit = UnitSerializer(read_only=True)
class Meta:
model = Employee
fields = ['id', 'user', 'hr_number', 'contract_type', 'company',
'tax_id_number', 'joining_date', 'job_title', 'skill', 'unit',
'department', 'identification_number', 'is_manager', 'active']
For writable nested serializer you need to define update or create methods:
class EmployeeSerializer(serializers.ModelSerializer):
user = UserSerializer()
contract_type = ContractSerializer(read_only=True)
company = CompanySerializer(read_only=True)
job_title = JobSerializer(read_only=True)
department = DepartmentSerializer(read_only=True)
skill = SkillSerializer(read_only=True)
unit = UnitSerializer(read_only=True)
class Meta:
model = Employee
fields = ['id', 'user', 'hr_number', 'contract_type', 'company',
'tax_id_number', 'joining_date', 'job_title', 'skill', 'unit',
'department', 'identification_number', 'is_manager', 'active']
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
if user_data:
instance.user.first_name = user_data.get('first_name')
instance.user.last_name = user_data.get('last_name')
# update other user's fields here
instance.user.save()
employee = super(EmployeeSerializer, self).update(instance, validated_data)
return employee
I copied https://pypi.python.org/pypi/fh-drf-friendship/0.1.1 (Because i don't know how to apply fh-drf-friendship on my project.)
views.py
class FriendViewSet(NestedViewSetMixin, mixins.ListModelMixin, mixins.CreateModelMixin,
mixins.DestroyModelMixin, viewsets.GenericViewSet):
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
request.data['to_user'] = self.get_parents_query_dict()['user']
serializer = FriendSerializer(data=request.data)
if serializer.is_valid():
instance = serializer.save()
headers = self.get_success_headers(serializer.data)
serializer = FriendReadSerializer(instance)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def get_object(self):
to_user = User.objects.get(pk=self.get_parents_query_dict().get('user'))
from_user = User.objects.get(pk=self.kwargs.get('pk'))
return Friend.objects.get(to_user=to_user, from_user=from_user)
serializers.py
class UserSerializer(serializers.ModelSerializer):
related_postwriter = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
related_commentwriter = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = User
fields = ('id', 'username', 'email', 'related_postwriter', 'related_commentwriter')
class FriendSerializer(rest_framework_common.serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(source='from_user', queryset=User.objects.all())
class Meta:
model = Friend
exclude = ('url', 'from_user',)
def create(self, validated_data):
fr = FriendshipRequest.objects.get(from_user=validated_data['from_user'], to_user=validated_data['to_user'])
if fr and fr.accept():
return Friend.objects.get(from_user=validated_data['from_user'], to_user=validated_data['to_user'])
class FriendReadSerializer(rest_framework_common.serializers.ModelSerializer):
user = UserSerializer(source='from_user')
created_at = serializers.DateTimeField(source='created')
class Meta:
model = Friend
exclude = ('url', 'from_user', 'to_user', 'created',)
django version == 1.9.7
django rest framework version == 3.4.1
python3 == 3.5
On urls.py, there are router.register(r'friend', views.FriendViewSet, base_name='friend')
And when i go http://127.0.0.1:8000/friend/ on web browser,
DoesNotExist at /friend/
User matching query does not exist. comes.
How can i solve it?