I am new to Django rest and serializers concept
I have this kind of DB structure
User 1->N Address,
User 1->N Phone
User is a django.contrib.auth.models.User. Address and Phone both models contains this field user_id = models.ForeignKey(User, on_delete=models.CASCADE)
In my UserSerializer looks like this
class UserSerializer(serializers.ModelSerializer):
address = AddressSerializer(many=False)
phone = PhoneSerializer(many=False)
class Meta:
model = User
fields = (
'id', 'username',
'first_name', 'last_name',
'email', 'password',
'address', 'phone')
def create(self, validated_data):
# Save user.
user = User.objects.create(
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
username=validated_data['username'],
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user.save()
# User's address
address_data = validated_data['address']
address_data['user_id'] = user.id
address = Address.objects.create(**address_data)
# User's phone
phone_data = validated_data['phone']
phone_data['user_id'] = user.id
phone = Phone.objects.create(**phone_data)
return user
And AddressSerializer:
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = (
'user_id', 'address', 'pin_code', 'state', 'city', 'country')
I am able to create User but while doing serializer.data I am getting this error:
AttributeError: Got AttributeError when attempting to get a value for field `address` on serializer `UserSerializer`.
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 'address'.
EDIT:
My create method looks like this
def create(self, validated_data):
password = validated_data.pop('password')
validated_data['username'] = validated_data['email']
user = User.objects.create(**validated_data)
user.set_password(password)
user.save()
return user
Your fields for address and phone should be called address_set and phone_set if you haven't specified related_name explicitly
class UserSerializer(serializers.ModelSerializer):
address_set = AddressSerializer(many=False)
phone_set = PhoneSerializer(many=False)
class Meta:
model = User
fields = (
'id', 'username',
'first_name', 'last_name',
'email', 'password',
'address_set', 'phone_set'
)
or
class UserSerializer(serializers.ModelSerializer):
address = AddressSerializer(many=False, source='address_set')
phone = PhoneSerializer(many=False, source='phone_set')
class Meta:
model = User
fields = (
'id', 'username',
'first_name', 'last_name',
'email', 'password',
'address', 'phone'
)
Related
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']
I have these 2 models:
# core/models.py
Class Certificate:
user = models.Foreignkey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL,
related_name='certificates')
name = models.Charfield()
.
.
# users/models.py
Class User(AbstractBaseUser):
.
.
and I want to send the certificates of a user along with its other pieces of information.
this is my serializer:
# users/serializers.py
class CertificationSerializer(serializers.ModelSerializer):
"""Serializer for the certificate object"""
class Meta:
model = Certificate
class UserSerializer(serializers.ModelSerializer):
"""Serializer for the user object"""
# TODO
# videos = serializers.PrimaryKeyRelatedField(
# many=True,
# queryset=Tag.objects.all()
# )
certificates = CertificationSerializer(many=True)
class Meta:
model = get_user_model()
# TODO add certificates and videos
fields = ('id', 'email', 'password', 'first_name', 'last_name',
'phone_number', 'credit', 'points', 'certificates')
extra_kwargs = {'password': {'write_only': True,
'label': 'گذرواژه', 'min_length': 5}}
read_only_fields = ('id', 'certificates',)
def create(self, validated_data):
"""Create a new user and return it"""
return get_user_model().objects.create_user(**validated_data)
def update(self, instance, validated_data):
"""Update a user and return it"""
password = validated_data.pop('password', None)
user = super().update(instance, validated_data)
if password:
user.set_password(password)
user.save()
return user
now when I want to create a new user it tells me that field certificates can not be empty, how can I make the certificates field not required?
I developed api with django. I created a code structure like below.
How do I get the user's first_name and last_name in class ProfileSerializer?
that is, with ProfileSerializer, I want to get information such as the user's name, surname, id number
``
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password
from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework import serializers
from account.models import Profile
class ProfileSerializer(ModelSerializer):
class Meta:
model = Profile
fields = ('id', 'userKey', 'phone', 'email', 'address', 'userState')
class UserSerializer(ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'profile')
def update(self, instance, validated_data):
profile = validated_data.pop('profile')
profile_serializer = ProfileSerializer(instance=instance.profile, data=profile)
profile_serializer.is_valid(raise_exception=True)
profile_serializer.save()
return super(UserSerializer, self).update(instance, validated_data)
``
Simply define serializers.CharField() as,
class ProfileSerializer(ModelSerializer):
first_name = serializers.CharField(source='user.first_name', read_only=True)
last_name = serializers.CharField(source='user.last_name' read_only=True)
class Meta:
model = Profile
fields = ('id', 'userKey', 'phone', 'email',
'address', 'userState', 'first_name', 'last_name')
Just change your serializer like this
class ProfileSerializer(ModelSerializer):
class Meta:
model = Profile
fields = ('id', 'userKey', 'phone', 'email', 'address', 'userState')
def to_representation(self, instance):
data = super().to_representation(instance)
data.update({'username': instance.userKey.username, 'first_name': instance.userKey.first_name, 'last_name': instance.userKey.last_name})
return data
how do I create user with default group? my serializer
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password')
def create(self, validated_data):
user = super(UserSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
views.py
class UserView(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
How do I set user group when creating from this view?
Just add something like
user.groups.add(Group.objects.get(name='Whatever'))
after user.save() in the create method.
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.