In my project I have created a custom User object with a one to one relation to a Profile object in order to isolate authentication fields.
Profile
class Profile(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
image = models.ImageField(default='/user_images/default.png', upload_to='user_images', blank=True, null=True)
def __str__(self):
return self.first_name + " " + self.last_name
User
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
profile = models.OneToOneField(Profile, related_name="user" ,on_delete=models.CASCADE, default=None, blank=True, null=True)
is_visible = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
My problem now is that i want to create the Profile object with the same request of the User object
View
class CustomView(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format='json'):
serializer = CustomSerializer(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)
Serializer
class CustomSerializer(serializers.Serializer):
# User
email = serializers.EmailField(required=True)
password = serializers.CharField(min_length=8, write_only=True)
# Profile
first_name = serializers.CharField(max_length=50)
last_name = serializers.CharField(max_length=50)
image = serializers.ImageField(allow_null=True, max_length=100, required=False)
def create(self, validated_data):
profile = Profile.objects.create(
first_name = validated_data['first_name'],
last_name = validated_data['first_name'],
image = validated_data['image'],
)
user = User.objects.create(
email = validated_data['email'],
profile = profile
)
user.set_password(validated_data['password'])
user.save()
return user
Now in the database everything is created correctly but i receive this error
Got AttributeError when attempting to get a value for field
first_name on serializer CustomSerializer. 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 'first_name'.
I suppose this means that a serializer can only handle the creation of one model so i'm supposed to handle my case?
Thanks
I assume that your error comes from to_representation() and what you can do is to set write_only=True on first_name, last_name and image fields like this:
first_name = serializers.CharField(max_length=50, write_only=True)
However, a more clean solution would be to use ModelSerialzier for both User and Profile models. If you do so, you'll have something like:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ("id", "first_name", "last_name", "image")
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ("id", "email", "profile", [...])
def create(self, validated_data):
...
Related
I'm new in DRF: ¿Can I create a filter in a function in Django Rest Framework, not created it in a Class, for use in a view? I'm trying a filter for search by: document and document_type.
Ty
My code:
#model
class Account(models.Model):
'''Modelo para la gestión de cuentas'''
document_type = models.CharField('Tipo documento', null=True, max_length=20, choices=DOCUMENT_TYPE)
document = models.CharField('Documento',null=True, max_length=18)
first_name = models.CharField('Nombres / razón social',null=True, max_length=100)
last_name = models.CharField('Apellidos', null=True, max_length=100)
phone = models.CharField('Teléfono fijo', max_length=7, null=True, blank=True)
cellphone = models.CharField('Celular', max_length=10, null=True, blank=True)
email = models.EmailField('Correo', max_length=100, null=True, blank=True )
address = models.CharField('Dirección', max_length=70, null=True, blank=True)
gender = models.CharField('Genero',null=True, max_length=1, choices=GENDER_TYPE)
affiliate_type = models.CharField('Tipo de afiliado',null=True, max_length=12, choices=AFFILIATE_TYPE)```
#view
```class AccountList(APIView):
"""
List all accounts, or create a new account....
"""
def get(self, request, format=None):
account = Account.objects.all()
serializer = AccountSerializer(account, many=True)
filter_class = AccountFilter
filter_backends = [filters.SearchFilter]
return Response({'data':serializer.data},status=status.HTTP_200_OK)
def post(self, request, format=None):
serializer = AccountSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'Message':'Registro creado exitosamente'}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)```
#filter
class AccountFilter(django_filters.rest_framework.FilterSet):
class Meta:
model = Account
fields = {
'document': ['exact'],
'document_type': ['exact']
}
ordering_fields = ('document')
You can use self.filter_queryset(queryset) to filter queryset using filter class.
class AccountList(APIView):
def get(self, request, format=None):
self.filter_class = AccountFilter
self.filter_backends = [filters.SearchFilter]
account_qs = Account.objects.all()
account = self.filter_queryset(account_qs)
serializer = AccountSerializer(account, many=True)
return Response({'data':serializer.data},status=status.HTTP_200_OK)
I have multiple users and I designed two models for two users and the rest is super user.
And I have the model User that holds common data for all users.
I want to save the patient post request to two different models: User and Patient; I tried these codes but, didn't work.
This is my Signup view
class PatientSignupView(generics.GenericAPIView):
serializer_class=PatientSignupSerializer
permission_classes = [
permissions.AllowAny
]
def post(self, request, *args, **kwargs):
user_serializer=self.get_serializer(data=request.data)
user_serializer.is_valid(raise_exception=True)
user=user_serializer.save()
return Response({
"user":UserSerializer(user, context=self.get_serializer_context()).data,
"patient":PatientSerializer(patient, context=self.get_serializer_context()).data,
"token":Token.objects.get(user=user).key,
"message":"account created successfully",
})
And here is my Signup Serializer class
class PatientSignupSerializer(serializers.ModelSerializer):
password_confirm = serializers.CharField(style={"input_type":"password"}, write_only=True)
class Meta:
model = User
fields = ['username', 'email', 'is_doctor', 'is_patient', 'password',
'password_confirm']
extra_fields = {
'password': {'write_only': True},
}
def save(self, **kwargs):
user=User(
username=self.validated_data['username'],
email=self.validated_data['email']
)
password=self.validated_data['password']
password_confirm=self.validated_data['password_confirm']
if password !=password_confirm:
raise serializers.ValidationError({"error":"password do not match"})
user.set_password(password)
user.is_patient=True
user.is_doctor=False
user.save()
PatientSerializer.serialized_data(user)
return user
Here is the PatientSerializer and UserSerializer class
from rest_framework import serializers
from .models import Patient, User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username','email','is_doctor','is_patient']
class PatientSerializer(serializers.ModelSerializer):
class Meta:
model = Patient
fields = ['first_name', 'middle_name', 'last_name', 'address', 'phone_number', 'birth_date', 'gender']
def serialized_data(self, user, **kwargs):
patient = Patient.objects.create(
user = user,
first_name=self.validated_data['first_name'],
middle_name = self.validated_data['middle_name'],
last_name = self.validated_data['last_name'],
address = self.validated_data['address'],
phone_number = self.validated_data['phone_number'],
birth_date = self.validated_data['birthh_date'],
gender = self.validated_data['gender']
)
return patient
Thank you!
It is solved like this
The signup view is:
class PatientSignupView(generics.GenericAPIView):
serializer_class=PatientSignupSerializer
def post(self, request, *args, **kwargs):
user_serializer=self.get_serializer(data=request.data)
user_serializer.is_valid(raise_exception=True)
user=user_serializer.save()
return Response({
"user":UserSerializer(user, context=self.get_serializer_context()).data,
"token":Token.objects.get(user=user).key,
"message":"account created successfully",
})
The serializer class will looks like:
class PatientSignupSerializer(serializers.ModelSerializer):
password_confirm = serializers.CharField(style={"input_type":"password"}, write_only=True)
first_name = serializers.CharField()
middle_name = serializers.CharField()
last_name = serializers.CharField()
address = serializers.CharField()
phone_number= serializers.CharField()
birth_date = serializers.DateTimeField()
gender = serializers.CharField()
class Meta:
model = User
fields = '__all__'
extra_fields = {'password': {'write_only': True},}
def save(self, **kwargs):
user=User(
username=self.validated_data['username'],
email=self.validated_data['email']
)
password=self.validated_data['password']
password_confirm=self.validated_data['password_confirm']
if password !=password_confirm:
raise serializers.ValidationError({"error":"password do not match"})
user.set_password(password)
user.is_patient=True
user.is_doctor=False
user.save()
Patient.objects.create(user=user,
first_name=self.validated_data['first_name'],
middle_name=self.validated_data['middle_name'],
last_name=self.validated_data['last_name'],
address=self.validated_data['address'],
phone_number=self.validated_data['phone_number'],
birth_date=self.validated_data['birth_date'],
gender=self.validated_data['gender'])
return user
I have two models Role and User. User model has a foreign-key field associated with model Role.
Model Role
class Role(models.Model):
role_name = models.CharField(max_length=255, blank=False, unique=True)
def __str__(self):
return 'Role Object ({})'.format(self.id, self.role_name)
Model User
class User(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField(max_length=255, blank=False)
last_name = models.CharField(max_length=255, blank=False)
email = models.EmailField(max_length=255, blank=False, unique= True)
phone_number = models.BigIntegerField(blank=False, unique= True)
password = models.TextField(blank=False)
company_name = models.CharField(max_length=255, null=True, unique=True)
is_active = models.BooleanField(default= False)
role = models.ForeignKey(Role, related_name='group_name', on_delete=models.CASCADE, blank=False)
business = models.ForeignKey(Businesses, on_delete=models.CASCADE, null=True)
objects = UserManager()
USERNAME_FIELD = 'email'
def __str__(self):
return 'User Object ({})'.format(self.id)
Using ModelSerializer and ListAPIView I want to get the list of all the users as shown below -
{
"first_name": "",
"last_name":"",
"email":"",
"phone_number":,
"is_active":"",
"role":{
"id":1,
"role_name": "Admin"
}
}
Also, I have created serializers -
class AdminSerializer(serializers.ModelSerializer):
role = RoleSerializer(read_only=True)
class Meta:
model = User
fields = ['id', 'first_name', 'last_name', 'email', 'phone_number', 'password', 'role', 'is_active' ]
read_only_field = ['role_name']
extra_kwargs = {
'password':{
'write_only':True
},
'is_active':{
'required':False
}
}
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class RoleSerializer(serializers.ModelSerializer):
class Meta:
model = Role
field = ['id', 'role_name']
What exactly should I do to my serializer to achieve desire output. What exactly should I write in my ListAPIView() what a ListAPIVIew returns.
class UsersListAPIView(ListAPIView):
queryset = User.objects.all()
serializer_class = AdminSerializer
def list(self, request):
queryset = self.get_queryset().filter(role=request.query_params.get['id']).prefetch_related("group_name")
serializer = AdminSerializer(queryset, many=True)
return Response(serializer.data)
This returns an error -
TypeError: 'method' object is not subscriptable
Models.py
class CustomTarget(models.Model):
target_red = models.CharField(max_length=64, blank=True, null=True)
target_amber = models.CharField(max_length=64, blank=True, null=True)
target_green = models.CharField(max_length=64, blank=True, null=True)
remarks = models.CharField(max_length=256, blank=True, null=True)
flag = models.BooleanField(default=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
project = models.ForeignKey(ProjectsMaster, on_delete=models.CASCADE)
metric = models.ForeignKey(MetricsMaster, on_delete=models.CASCADE)
class Meta:
db_table = 'custom_target'
unique_together = (('user', 'project', 'metric'),)
Serializers.py
class CustomTargetSerializer(serializers.ModelSerializer):
"""
"""
logger.info("Control entered into CustomTargetSerializer class")
class Meta:
model = CustomTarget
fields = ('id', 'target_red', 'flag', 'user', 'project', 'target_amber', 'target_green','remarks', 'metric')
def create(self, validated_data):
"""
:param validated_data:
:return:
"""
data = CustomTarget(target_red=validated_data["target_red"],
user=User(validated_data["user"]),
project=ProjectsMaster(validated_data["project"]),
target_amber=validated_data["target_amber"],
target_green=validated_data["target_green"],
remarks=validated_data["remarks"],
metric=MetricsMaster(validated_data["metric"]),
flag=True, )
data.save()
return data
views.py
#api_view(["PUT"])
#renderer_classes((JSONRenderer,))
def custom_target(request):
"""
To add new data and update existing data in custom_target table.
:param request:
:return:
"""
logger.info("Control entered into custom_target")
return_dict = dict()
try:
metric_data = request.data
metric_config_obj = matrix_config_filter(metric_data)
if metric_config_obj.exists() is True:
if metric_config_obj[0].flag is True:
custom_serializer = CustomTargetSerializer(data=request.data)
custom_tar_obj = CustomTarget.objects.filter(metric=request.data['metric'])
if custom_tar_obj.exists():
custom_serializer.update(validated_data=request.data, instance=custom_tar_obj)
else:
if custom_serializer.is_valid(raise_exception=ValueError):
custom_serializer.create(validated_data=request.data)
return_dict["msg"] = "success"
status_msg = status.HTTP_200_OK
except Exception as e:
logger.error("Error from custom_target: %s" % e)
return_dict["msg"] = "failed"
status_msg = status.HTTP_400_BAD_REQUEST
return Response(return_dict, status=status_msg)
here,now i am giving hard coded user id,but i want to save user id of the login user in the create() of Serializer.
here user is saved in auth_user table and have foreign key relation with Custom_target table.
I tried so many things but i am not getting the request.user in Serializer.
You could use PrimaryKeyRelatedField along with CurrentUserDefault as
class CustomTargetSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault())
# your code
UPDATE-1 : Complete Serializer
class CustomTargetSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault())
class Meta:
model = CustomTarget
fields = ('id', 'target_red', 'flag', 'user', 'project', 'target_amber', 'target_green', 'remarks', 'metric')
def create(self, validated_data):
user = validated_data.pop('user')
project = validated_data.po('project')
metric = validated_data.pop('metric')
instance = CustomTarget.objects.create(
user=user,
project=project,
metric=metric,
**validated_data
)
return instance
models.py
class UserProfile(models.Model):
contact_no = models.CharField(max_length=20, name='contact_no')
token_key = models.CharField(max_length=128, blank=True, null=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.CharField(max_length=128, blank=True, null=True, name='role')
class Meta:
db_table = 'user_profile'
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'email', 'password',)
extra_kwargs = {'password': {'write_only': True}}
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = UserProfile
fields = ('user', 'contact_no', 'role',)
def create(self, validated_data):
"""
Overriding the default create method of the Model serializer.
:param validated_data: data containing all the details of profile
:return: returns a successfully created profile record
"""
user_data = validated_data.pop('user')
user = UserSerializer.create(UserSerializer(),validated_data=user_data)
profile, created = UserProfile.objects.update_or_create(user=user, contact_no=validated_data.pop('contact_no'),
role=validated_data.pop('role'))
return profile
views.py
class UserRecordsView(APIView):
"""
A class based view for creating and fetching profile records
"""
def get(self, request):
"""
Get all the student records
:param format: Format of the profile records to return to
:return: Returns a list of profile records
"""
profiles = UserProfile.objects.all()
serializer_context = {
'request': request,
}
serializer = UserProfileSerializer(profiles, many=True, context=serializer_context)
return Response(serializer.data)
def post(self, request):
"""
:User and User Profile Creation .
:param request:
:return:
"""
serializer = UserProfileSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.create(validated_data=request.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.error_messages,
status=status.HTTP_400_BAD_REQUEST)
Here password is in the form of text ,i want encrypted form of password.here one to one relation is there between user and userprofile table.i want to create user and userprofile data by only one method, that's why i need to encrypt password also at the same time only.
I got the answer.
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(required=True)
class Meta:
model = UserProfile
fields = ('user', 'contact_no', 'role',)
def create(self, validated_data):
"""
Overriding the default create method of the Model serializer.
:param validated_data: data containing all the details of profile
:return: returns a successfully created profile record
"""
user_data = validated_data.pop('user')
user = UserSerializer.create(UserSerializer(),validated_data=user_data)
user.set_password(user.password)
user.save()
profile, created = UserProfile.objects.update_or_create(user=user, contact_no=validated_data.pop('contact_no'),
role=validated_data.pop('role'))
return profile
we need to add only two lines to get the encrypted password here.
user.set_password(user.password)
user.save()