Django 2 - Add User Error - group with this name already exists - django

When I try to add a User with a list of groups I get an error stating that "A group with this name already exists". Here are my params:
{
'email': 'test#test.com',
'first_name': 'Bob',
'last_name': 'Jones',
'groups': [{'url': 'http://localhost:8000/api/groups/1/', 'name': 'Admin'}]
}
serializers.py
class GroupSerializer(serializers.HyperlinkedModelSerializer):
"""
Serializer to interact with the Groups model.
"""
class Meta:
model = Group
fields = ('url', 'name', 'id')
class UserSerializer(serializers.HyperlinkedModelSerializer):
"""
Serializer to interact with the Users model.
"""
url = serializers.HyperlinkedIdentityField(view_name='users-detail')
groups = GroupSerializer(many=True)
class Meta:
model = User
fields = ('id', 'url', 'username', 'email',
'groups', 'first_name', 'last_name', 'is_superuser', 'is_staff', 'is_active')
views.py
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
serializer_class = UserSerializer
queryset = User.objects.all()
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

remove nicely the validator for group name:
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
fields = ('name',)
extra_kwargs = {
'name': {'validators': []},
}

Related

ImageField gets None value after overriding create method

I am trying to create a user, I need to override the create method, but after that, imagefield gets null value instead of given file
here is my code
views.py
class VerifyEmail(CreateAPIView):
serializer_class = UserSerializer
queryset = User.objects.none()
def create(self, request, *args, **kwargs):
print(request.POST.get("profile_picture"))
token = request.GET.get("token")
invite_info = Invitation.objects.get(new_token=token)
data = {
'email': invite_info.receiver,
'organization': invite_info.organization.pk,
'is_staff': request.GET.get('is_staff', False),
'is_superuser': request.GET.get('is_superuser', False),
'first_name': request.POST.get('first_name', ''),
'last_name': request.POST.get('last_name', ''),
'profile_picture': request.POST.get('profile_picture'),
'country': request.POST.get('country'),
'password': request.POST.get('password')
}
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
serializer.save()
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=201, headers=headers)
serilizers.py
# class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(
write_only=True,
required=True,
style={'input_type': 'password', 'placeholder': 'Password'}
)
class Meta:
model = User
fields = ('id', 'email', 'organization', 'first_name', 'last_name',
'country', 'profile_picture', 'date_joined', 'modification_date', "is_active",'password')
def create(self, validated_data):
validated_data['password'] = make_password(validated_data.get('password'))
return super(UserSerializer, self).create(validated_data)
imagefield in models.py
profile_picture = models.ImageField(upload_to="images",)
It's work fine when I use default implementation of create method,but when I try to override it dictionary data gets all values except "profile_picture" which gets None. please help me.
You should be able to get the file from request.FILES, not request.POST.
But, rather than overriding the View's create function like that, why not override the serializer's create function like this:
class ABCSerializer():
password = serializers.CharField(
write_only=True,
required=True,
style={'input_type': 'password', 'placeholder': 'Password'}
)
class Meta:
model = User
fields = ('id', 'email', 'organization', 'first_name', 'last_name',
'country', 'profile_picture', 'date_joined', 'modification_date', "is_active",'password')
def create(self, validated_data):
validated_data['password'] = make_password(validated_data.get('password'))
request = self.context['request']
token = request.GET.get("token")
invite_info = Invitation.objects.get(new_token=token)
validated_data['email'] = invite_info.receiver
validated_data['organization'] = invite_info.organization.pk return super(UserSerializer, self).create(validated_data)
In this way, you can remove the override of create function from view.
Finally, (optional) rather than using request.GET, why not pass the token information through URL argument ie path('/something/<token:str>', YourView.as_view()) and access that value through self.kwargs['token']

May not set both `read_only` and `write_only`

In my project I want to set password as both read_only(because I have a separate endpoint to reset password) and write_only(because I don't want password send in the response).
Here is my serializer:
class UserSerializer(serializers.ModelSerializer):
"""A Serizlier class for User """
class Meta:
model = models.User
fields = ('id', 'email', 'phone_number', 'user_type', 'password')
extra_kwargs = { 'password': { 'write_only': True} }
read_only_fields = ('password',)
But I get an error saying:
AssertionError at /api/user/21/
May not set both read_only and
write_only
How can I have a field be both read_only and write_only?
Override the __init__() method of the serializer as,
class UserSerializer(serializers.ModelSerializer):
"""A Serizlier class for User """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.context['some_flag']:
self.fields['password'].read_only = True
else:
self.fields['password'].write_only = True
class Meta:
model = models.User
fields = ('id', 'email', 'phone_number', 'user_type', 'password')
# extra_kwargs = { 'password': { 'write_only': True} } # remove this
# read_only_fields = ('password',) # remove this
The some_flag variable is something that you should pass to the serializer either from the password reset view or from the other view
So extending on the answer of #JPG
The Serializer
class ProfileSerializer(serializers.ModelSerializer):
name = serializers.CharField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'create_method' in self.context:
self.fields['name'].write_only = True
else:
self.fields['name'].read_only = True
And the view set
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
def get_serializer_context(self):
# Call Parent
context = super().get_serializer_context()
# Check If This Is a POST
if self.request.method == 'POST':
context['create_method'] = True
# Handoff
return context
This will allow name to be written on POST and read only on everything else
well - normally if you have two endpoints using a similar serializer which needs to differ only with a certain field/functionality you would create a base class and abstract it and only change/modify the parts of it that need to change. Here is what I would do.
class (serializers.ModelSerializer):
"""A Serizlier class for User """
class Meta:
model = models.User
fields = ('id', 'email', 'phone_number', 'user_type', 'password')
extra_kwargs = { 'password': { 'read_only': True} }
class UserSerializerForOtherView(UserSerializer):
class Meta(UserSerializer.Meta):
extra_kwargs = { 'password': { 'write_only': True} }
Now UserSerializerForOtherView inherits the same behaviour as UserSerializer and you now also have a new serializer should you want to further expand on functionality on this serializer alone.
All you will need to do is tell the other view to use the other serializer.

DRF serializer.save() not saving to database

I have an api that will do a patch on a resource (MyUser). It validates ok and seems to save the object, however when querying the database the changes have not been saved.
class UserSignupView(generics.UpdateAPIView):
serializer_class = MyUserSerializer
def get_object(self, email):
obj = MyUser.objects.get(email=email)
self.check_object_permissions(self.request, obj)
return obj
def patch(self, request):
print(request.user)
user = self.get_object(request.user.email)
print(user.street)
serializer = MyUserSerializer(user, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
savedUser = MyUser.objects.get(email=request.user.email)
print(savedUser.street)
print(serializer.data)
return Response(serializer.data)
class MyUserSerializer(serializers.ModelSerializer):
class Meta:
model = MyUser
fields = (
'id', 'first_name', 'last_name', 'email', 'phone_number', 'street', 'locality', 'city',
'county', 'postcode')
Looking at the print statements I get:
user#example.com
None
123 Fake Street
MyUser object
It returns the correct serialised data which contains the changes but the database does not have the changes. The database connection is ok as I can query it and make other reads/writes/etc. It's pretty much the same as the UpdateModelMixin except I've had to override the get_object with a passed in parameter.
Try to override the update method and see what happens:
class MyUserSerializer(serializers.ModelSerializer):
class Meta:
model = MyUser
fields = (
'id', 'first_name', 'last_name', 'email', 'phone_number', 'street', 'locality', 'city',
'county', 'postcode')
def update(self, instance, validated_data):
instance.first_name = validated_data.get('first_name',instance.first_name)
instance.last_name = validated_data.get('last_name',instance.last_name)
instance.email = validated_data.get('email',instance.email)
instance.phone_number = validated_data.get('phone_number',instance.phone_number)
instance.street = validated_data.get('street',instance.street)
instance.locality = validated_data.get('locality',instance.locality)
instance.city = validated_data.get('city',instance.city)
instance.county = validated_data.get('county',instance.county)
instance.postcode = validated_data.get('postcode',instance.postcode)
instance.save()
return instance

Send Custom Error response from serializer in django rest framework?

I want to send a custom response from serializers create view to front-end of my application. I tried rest framework Response tutorials but it does not work. My code is:
class UserSerializer(serializers.ModelSerializer):
"""Serializer to serialize user model object"""
class Meta:
model = User
fields = ('id', 'username', 'password', 'first_name', 'last_name')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
"""create a new user"""
firstname = self.initial_data['first_name']
lastname = self.initial_data['last_name']
fullname = str(firstname) +" "+ str(lastname)
email = self.initial_data['username'].lower()
try:
customer = User.create(
name=fullname,
email=email)
except Error as e:
error = {'message': e._message or 'Unknown error'}
return Response(error,status=status.HTTP_400_BAD_REQUEST)
serializers.py
class UserSerializer(serializers.ModelSerializer):
"""Serializer to serialize user model object"""
class Meta:
model = User
fields = ('id', 'username', 'password', 'first_name', 'last_name')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
"""create a new user"""
firstname = validated_data['first_name']
lastname = validated_data['last_name']
fullname = str(firstname) +" "+ str(lastname)
email = validated_data['username'].lower()
try:
customer = User.objects.create(
name=fullname,
email=email)
return customer
except Exception as e:
error = {'message': ",".join(e.args) if len(e.args) > 0 else 'Unknown Error'}
raise serializers.ValidationError(error)
So you want to change the representation of the User (the shape of the JSON response) from this view. To do this you need to change the .to_representation(self, obj) method in the serializer:
class UserSerializer(serializers.ModelSerializer):
"""Serializer to serialize user model object"""
class Meta:
model = User
fields = ('id', 'username', 'password', 'first_name', 'last_name',)
write_only_fields = ('id', 'password',)
def to_representation(self, obj):
return {
'firstname': obj.first_name,
'lastname': obj.last_name,
'fullname': obj.first_name+' '+obj.last_name,
'email': obj.username.lower()
}
This should mean that whenever a request is made that uses this serializer the user will only ever see those 4 fields in the JSON, e.g.
{
"firstname":"Salman",
"lastname": "Ahmed",
"fullname": "Salman Ahmed",
"email": "salman_ahmed#email.com"
}
More details on this is hidden in their docs here.

How to choose a different serializer class in genericviewsets based on the data sent by the front end

thee are my serializers for different users
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = player
fields = ('contact_name', 'contact_email', 'address',)
class ManagerSerializer(serializers.ModelSerializer):
class Meta:
model = Manager
fields = ('contact_name', 'contact_email', 'address',
'city', 'country',)
class CoachSerializer(serializers.ModelSerializer):
class Meta:
model = Shipper
fields = (' 'contact_name', 'contact_email', 'address', 'city', 'about', 'country',)
these are my user Serializers with relation with each user type
class playerUserSerialzers(serializers.ModelSerializer):
player =PlayerSerializer()
class Meta:
model = MyUser
fields = ('email', 'id', 'password', 'player',)
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
player_data = validated_data.pop('player')
user = MyUser(
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user = MyUser.objects.create(**validated_data)
Player.objects.create(user=user, **player_data)
user.save()
return user
class managerUserSerialzers(serializers.ModelSerializer):
manager =ManagerSerializer()
class Meta:
model = MyUser
fields = ('email', 'id', 'password', 'manager',)
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
manager_data = validated_data.pop('manager')
user = MyUser(
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user = MyUser.objects.create(**validated_data)
Manager.objects.create(user=user, **manager_data)
user.save()
return user
class managerUserSerialzers(serializers.ModelSerializer):
coach =CoachSerializer()
class Meta:
model = MyUser
fields = ('email', 'id', 'password', 'coach',)
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
coach_data = validated_data.pop('coach')
user = MyUser(
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user = MyUser.objects.create(**validated_data)
Coach.objects.create(user=user, **coach_data)
user.save()
return user
this doesn't work but i would like to use different serializers based on the front end request
class MyUserViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
GenericViewSet):
queryset = MyUser.objects.all()
pagination_class = LimitOffsetPagination
permission_classes = (AllowAny,)
how do i switch bases on serializer class here
def get_serializer_class(self,):
if self.request.query_params.get("player", None):
return MyBrokerUserSerialzers
From the documentation you can specify the serializer_class property telling the right serializer you want.
Then, as you are inheriting many mixing classes you can overwrite their methods and get the information from your frontend. For each kind of request method you will have to get the frontend information in a different way.
class MyUserViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
GenericViewSet):
queryset = MyUser.objects.all()
pagination_class = LimitOffsetPagination
permission_classes = (AllowAny,)
def define_serializer_class(self, serializer_class_name):
serializer_class = serializer_class_name
def get(self, request, *args, **kwargs):
#here in GET METHOD you wil user the RetrieveModelMixin
#So get the kind of serializer from you get params in request.GET["param name"]
#and set the right one with a 'if' condition and calling
# self.define_serializer_class(<the name of the right serializer class>)
return self.retrieve(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
#here in POST METHOD you wil user the CreateModelMixin
#So get the kind of serializer from you post playload in request.POST["param name"]
#and set the right one with a 'if' condition and calling
# self.define_serializer_class(<the name of the right serializer class>)
return self.update(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
#Do the same here get the info from the frontend
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
#Do the same here get the info from the frontend
return self.destroy(request, *args, **kwargs)
I'm sorry I cannot tell the right code to do what you want, but
the comments in the code explain step-by-step a way to solve your problem. I never have done it myself because I don't like to use mixings (they make up everying), but it will work for you. Just find the right way to get the information from the frontend for each kind of HTTP method that the mixins use and set the serializer_class as I said above.
I hope it helps you.
In my opinion this is unclear what you are trying to achieve and why are you doing things this way.
Let me put it in the perspective, if you have 3 seaparate objects, create 3 separate backends for them and you do not need anything else.
class UserMixin(object):
def create(self, validated_data):
user = MyUser(email=validated_data.pop('email'))
user.set_password(validated_data.pop('password')
user.save() # now you can use user in relations
validated_data['user'] = user
super(UserMixin, self).create(**validated_data)
return user
class PlayerSerializer(serializers.ModelSerializer, UserMixin):
class Meta:
model = Player
fields = ('id', 'email', 'password', 'contact_name', 'contact_email',
'address')
read_only_fields = ('id',)
extra_kwargs = {'password': {'write_only': True}}
class ManagerSerializer(serializers.ModelSerializer, UserMixin):
class Meta:
model = Manager
fields = ('id', 'email', 'password', 'contact_name', 'contact_email',
'address', 'city', 'country')
read_only_fields = ('id',)
extra_kwargs = {'password': {'write_only': True}}
class CoachSerializer(serializers.ModelSerializer, UserMixin):
class Meta:
model = Shipper
fields = ('id', 'email', 'password', 'contact_name', 'contact_email',
'address', 'city', 'about', 'country',)
read_only_fields = ('id',)
extra_kwargs = {'password': {'write_only': True}}
# now 3 viewsets
class PlayerViewset(viewsets.ModelViewSet):
model = Player
queryset = Player.objects.all()
serializer_class = PlayerSerializer
class ManagerViewset(viewsets.ModelViewSet):
model = Manager
queryset = Manager.objects.all()
serializer_class = ManagerSerializer
class CoachViewset(viewsets.ModelViewSet):
model = Shipper
queryset = Shipper.objects.all()
serializer_class = CoachSerializer
# register api ends
api_router.register('player', PlayerViewset)
api_router.register('manager', ManagerViewset)
api_router.register('coach', CoachViewset)
simple, efective and not much work...
p.s. class names starts with capital letter - please read pep-8