ValueError: Cannot assign "2": "Model.field" must be a "Model" instance - django

I have a registration system that works on otp.
I have a custom user model
class User(AbstractUser):
password = models.CharField(max_length=128, blank=True, null=True)
email = models.EmailField(max_length=254, unique=True)
dial_code_id = models.CharField(max_length=100)
mobile_number = models.CharField(max_length=100, blank=True, null=True)
username = models.CharField(max_length=150, unique=True, blank=True, null=True)
is_resource = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
skills = models.ManyToManyField(Skills)
class Meta:
db_table = "my_user"
def __str__(self):
return self.mobile_number
I have created an 'otp' model to save otp.
class Otp(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
otp = models.IntegerField()
created_on = models.DateTimeField(default=django.utils.timezone.now)
class Meta:
db_table = "otp"
My views looks like this
class UserCreateResponseModelViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
custom_data = {
"status": True,
"message": 'Successfully registered your account.',
"data": serializer.data
}
generate_otp(serializer.data['id'])
return Response(custom_data, status=status.HTTP_201_CREATED)
else:
custom_data = {
"status": False,
"message": serializer.errors,
}
return Response(custom_data, status=status.HTTP_200_OK)
class UserCreateViewSet(UserCreateResponseModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
I have a function to generate and save otp
def generate_otp(user_id):
try:
otp_obj = Otp.objects.get(user_id=user_id)
except Otp.DoesNotExist:
otp_obj = None
if otp_obj is None:
otp = random.randrange(1000, 9999)
otp_obj = Otp(user=user_id, otp=otp)
otp_obj.save()
Serializer looks like
class UserSerializer(serializers.ModelSerializer):
mobile_number = serializers.CharField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
class Meta:
model = User
fields = ['id', 'first_name', 'last_name', 'email', 'dial_code_id','mobile_number', 'is_resource', 'is_customer']
The user is being registered successfully, but when it comes to saving otp in 'generate_otp()' I'm getting an error like
raise ValueError(ValueError: Cannot assign "2": "Otp.user" must be a "User" instance.
How can I overcome this?
Is this the right way to do it?

It looks like the problem is here:
otp_obj = Otp(user=user_id, otp=otp)
you're assigning an id (int) to the user field. not the user_id field

Related

QuerySet object has no attribute "email"

I am trying to get a list of all pathologists in my system. I need to filter the user on 2 basis i-e is_pathologist and Lab_Id=request.data[email]
I have tried switching between filter and get but then I get
Authentication.models.User.MultipleObjectsReturned: get() returned more than one User -- it returned 12!
Error traceback here
This is the code of my view
#api_view(['POST'])
def getAllPathologists(request):
user = get_user_model().objects.get(is_pathologist=True)
# If user exists, get the employee
print("user is: ", user)
pathologist = Employee.objects.get(user=user.email, Lab_Id=request.data['email'])
pathologistSerializer = EmployeeSerializer(pathologist, many=True)
return Response(pathologistSerializer.data)
This is user model
class User(AbstractUser):
# Add additional fields here
id = None
email = models.EmailField(max_length=254, primary_key=True)
name = models.CharField(max_length=100)
password = models.CharField(max_length=100)
contact_number = models.CharField(max_length=100)
is_patient = models.BooleanField(default=False)
is_doctor = models.BooleanField(default=False)
is_homesampler = models.BooleanField(default=False)
is_pathologist = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_lab = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now=True,editable=False)
last_login = models.DateTimeField(auto_now=True)
first_name = None
last_name = None
username = None
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'password']
objects = CustomUserManager()
def __str__(self):
return self.email
# Ensure that the password is hashed before saving it to the database
def save(self, *args, **kwargs):
self.password = make_password(self.password)
super(User, self).save(*args, **kwargs)
def has_perm(self, perm, obj=None):
return self.is_superuser
This is Employee model
class Employee(models.Model):
user = models.OneToOneField(
get_user_model(), on_delete=models.CASCADE, primary_key=True)
CNIC = models.CharField(max_length=100, unique=True)
Lab_Id = models.ForeignKey(Lab, on_delete=models.CASCADE)
def __str__(self):
return self.user.name
This is employee serializer
class EmployeeSerializer(serializers.ModelSerializer):
userData = UserSerializer(read_only=True, source='user')
email = serializers.EmailField(write_only=True)
password = serializers.CharField(write_only=True)
name = serializers.CharField(write_only=True)
contact_number = serializers.CharField(write_only=True)
is_homesampler = serializers.BooleanField(write_only=True)
class Meta:
model = Employee
# fields = " __all__"
fields = ["CNIC", "Lab_Id", "userData",
"name", "contact_number", "email", "password", "is_homesampler"]
def create(self, validated_data):
print("validated data = ", validated_data)
email = validated_data.pop("email")
password = validated_data.pop("password")
name = validated_data.pop("name")
contact_number = validated_data.pop("contact_number")
is_homesampler = validated_data.pop("is_homesampler")
user = get_user_model().objects.create_user(
email=email, password=password, name=name, contact_number=contact_number)
if (is_homesampler):
user.is_homesampler = True
else:
user.is_pathologist = True
user.save()
EmployeeObj = Employee.objects.create(user=user, **validated_data)
return EmployeeObj
You are getting objects and querysets conflated, filter() will return a queryset whereas get() tries to return an object. Below are the reasons for your errors:
The reason for your error with filter() is that a queryset is essentially a group of user objects. The queryset itself has no attribute email, but each user object within the group would. You therefore need to extract a single user from the queryset using first() or last(), for example.
Your error with get() is that your parameters are too broad and thus 12 users are returned. You need to adjust your code to handle this, it's usually done with either a try/except block or using the get_object_or_404 Django shortcut. Once you successfully get the user object, you can call user.email without issue.

request.user returning AnonymousUser in ModelViewSet, but working properly in APIView

I am trying to customize get_queryset() in my DocumentViewSet so the GET method will return all Document objects created by request.user (currently logged in user).
However, I am stuck in this error:django.core.exceptions.ValidationError: ['“AnonymousUser” is not a valid UUID.']
I assume this is caused by getting AnonymousUser as my self.request.user. The weird part is that my other APIView that deals with request.user are working flawlessly; The only difference I could find between the two is type of viewset: ModelViewSet vs APIView.
Would appreciate any help!
document.views
class DocumentViewSet(viewsets.ModelViewSet):
model = Document
serializer_class = DocumentSerializer
list_serializer_class = DocumentListSerializer
permission_classes = (AllowAny,)
def get_queryset(self):
user = self.request.user
return Document.objects.filter(user=user)
def get_serializer_class(self):
if self.action == "list":
if hasattr(self, "list_serializer_class"):
return self.list_serializer_class
return super(DocumentViewSet, self).get_serializer_class()
def perform_create(self, serializer):
print(self.request.user)
serializer.save(user=self.request.user)
document.serializers
class DocumentSerializer(serializers.ModelSerializer):
id = HashidSerializerCharField(source_field="documents.Document.id", read_only=True)
question_blocks = QuestionBlockSerializer(many=True)
outline_blocks = OutlineBlockSerializer(many=True)
source_cards = SourceSerializer(many=True, required=False)
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault()
)
class Meta:
model = Document
fields = "__all__"
def create(self, validated_data):
question_blocks = validated_data.pop("question_blocks")
outline_blocks = validated_data.pop("outline_blocks")
source_cards = validated_data.pop("source_cards")
document = Document.objects.create(**validated_data)
for qBlock in question_blocks:
QuestionBlock.objects.create(document=document, **qBlock)
for oBlock in outline_blocks:
OutlineBlock.objects.create(document=document, **oBlock)
for sCard in source_cards:
Source.objects.create(document=document, **sCard)
document.save()
return document
class DocumentListSerializer(serializers.ModelSerializer):
id = HashidSerializerCharField(source_field="documents.Document.id", read_only=True)
class Meta:
model = Document
fields = ("id", "title", "template", "updated")
document.models
class Document(models.Model):
id = HashidAutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
title = models.CharField(max_length=100, default="Untitled")
template = models.CharField(max_length=100, default="")
editorState = models.JSONField(default=[])
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
user.models
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
username = None
first_name = models.CharField(max_length=100, default="unknown")
last_name = models.CharField(max_length=100, default="unknown")
profile_pic = models.CharField(max_length=200, default="unknown")
email = models.EmailField(unique=True, db_index=True)
secret_key = models.CharField(max_length=255, default=get_random_secret_key)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
class Meta:
swappable = "AUTH_USER_MODEL"
users.api & selectors
This is the APIView which returns information of the user currently logged-in; It is working flawlessly.
# users.api
class UserMeApi(APIView):
def get(self, request, *args, **kwargs):
return Response(user_get_me(user=request.user))
# users.selectors
def user_get_me(*, user: User):
return {
"id": user.id,
"name": user.name,
"email": user.email,
"first_name": user.first_name,
"last_name": user.last_name,
"profile_pic": user.profile_pic,
}
EDIT: added document.serializer and rest of the viewset code
My mistake! The origin of the problem was on the frontend; I forgot to include withCredentials:true in axios GET request.
axios
.get<DocumentList>(
`${process.env.NEXT_PUBLIC_API_URL}/drafts/?format=json`,
{
withCredentials: true, //forgot this part!
}
)
.then(function (response) {
setDrafts(response.data.results);
});

Django Rest Framework partial=True not working

I am a beginner in Django & DRF and I have a very dumb problem with my project, which doesn't allow me to do the partial update. I am using generic views (UpdateAPIView) and I have been stuck with it for 2 weeks now.
It allows me to update, however, I have to fill every field but what I want to do is to pop email & mobile numbers if they are the same as the stored value in the database.
Hoping someone might help and thank you in advance.
Model:
class BusinessPartner(models.Model):
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
mobile_number = models.CharField(max_length=150, unique=True)
email = models.EmailField(max_length=150, unique=True)
business_name = models.CharField(max_length=255, blank=True)
type_of_business = models.CharField(max_length=150, blank=True)
street_address = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=150, blank=True)
state_province = models.CharField(max_length=150, blank=True)
postal_zip = models.IntegerField(null=True, blank=True)
services_offered = models.TextField(null=True, blank=True)
account = models.ForeignKey(Account, on_delete=models.CASCADE)
class Meta:
ordering = ["-account"]
def __str__(self):
return self.first_name + " " + self.last_name
Serializer:
class BusinessPartnerSerializer(serializers.ModelSerializer):
class Meta:
model = BusinessPartner
fields = [
"id",
"first_name",
"last_name",
"mobile_number",
"email",
"business_name",
"type_of_business",
"street_address",
"city",
"state_province",
"postal_zip",
"services_offered",
"account",
]
extra_kwargs = {"id": {"read_only": True}}
def update(self, instance, validated_data):
partner = BusinessPartner.objects.get(account=instance)
print("Previous mobile number: ", getattr(partner, "mobile_number"))
print("New mobile number: ", validated_data["mobile_number"])
if getattr(partner, "mobile_number") == validated_data["mobile_number"]:
validated_data.pop("mobile_number")
if getattr(partner, "email") == validated_data["email"]:
validated_data.pop("email")
for (key, value) in validated_data.items():
setattr(partner, key, value)
partner.save()
return partner
View:
class UpdatePartnerProfileView(generics.UpdateAPIView):
permission_classes = [IsAuthenticated]
serializer_class = BusinessPartnerSerializer
queryset = BusinessPartner.objects.all()
def update(self, request, *args, **kwargs):
serializer = self.serializer_class(
request.user, data=request.data, partial=True
)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.error, status=status.HTTP_400_BAD_REQUEST)

This field must be unique error in postman in OnetoOneField in Django Rest Framework

I am trying to update Customer Profile also updating main Customuser first_name and last_name field at the same time using nested serialization. But I am getting customer field must be unique error.
I have posted the pics here.
My models:
class CustomUser(AbstractUser):
# username = None
first_name = models.CharField(max_length=255, verbose_name="First name")
last_name = models.CharField(max_length=255, verbose_name="Last name")
email = models.EmailField(unique=True)
is_seller = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["first_name", "last_name"]
objects = CustomUserManager()
def __str__(self):
return self.email
class Customer(models.Model):
customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
full_name = models.CharField(max_length=100, blank=True)
phone_num = models.CharField(max_length=50, blank=True)
#dob = models.CharField(max_length=255,blank=True,null=True)
region = models.CharField(max_length=255, blank=True,null=True)
city = models.CharField(max_length=255, blank=True, null=True)
area = models.CharField(max_length=255,blank=True,null=True)
address = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.customer.email
My serializers:
class CustomerProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
# depth = 1
class CustomerUpdateSerializer(serializers.ModelSerializer):
customer = CustomerProfileSerializer()
class Meta:
model = User
fields = ('id', "first_name", "last_name",'customer')
def update(self,request, instance, validated_data):
user = self.request.user
instance.user.first_name=user.get('first_name')
instance.user.last_name = user.get('last_name')
instance.user.save()
customer_data = validated_data.pop('customer',None)
if customer_data is not None:
instance.customer.region = customer_data['region']
instance.customer.city = customer_data['city']
instance.customer.area = customer_data['area']
instance.customer.address = customer_data['address']
instance.customer.save()
return super().update(instance,validated_data)
My views:
class CustomerUpdateView(UpdateAPIView):
permission_classes = [IsAuthenticated]
queryset = User.objects.all()
serializer_class = CustomerUpdateSerializer
The url is visible in postman put request. I have sent the raw json data in the postman, but it throws this error. How to update those 4 fields in customer and 2 fields (first_name and last_name) from User model??
My code worked after I made some changes to the serializer.
Here is the working code:
class CustomerUpdateSerializer(serializers.ModelSerializer):
customer = CustomerProfileSerializer(many=False)
class Meta:
model = User
fields = ('id', "first_name", "last_name",'customer')
depth = 1
def update(self, instance, validated_data):
user = self.context['request'].user
user.first_name = validated_data.get('first_name')
user.last_name = validated_data.get('last_name')
user.save()
customer_data = validated_data.pop('customer',None)
if customer_data is not None:
instance.customer.region = customer_data['region']
instance.customer.city = customer_data['city']
instance.customer.area = customer_data['area']
instance.customer.address = customer_data['address']
instance.customer.save()
return super().update(instance,validated_data)
Its because I was using instance of user and same instance to customer model. Now, I get the fields first_name and last_name separately and use instance for customer only.It worked.

Django REST overrride destroy method to make user inactive

I'm trying to first access the users table via the user foreign key present in userinformations models and later override the RetriveUpdateDestroy API view's destroy method to change the status of the user to inactive instead of deleting them. I can't seem to access the is-active field of the in built User database.
views.py
class UserUpdateApiView(RetrieveUpdateDestroyAPIView):
queryset = UserInformation.objects.all()
serializer_class = UserInformationUpdateSerializer
lookup_field = 'pk'
lookup_url_kwarg = 'id'
def destroy(self, request, *args, **kwargs):
try:
user = User.objects.get(pk=self.kwargs["id"])
deleteStatusVal = False
user.is_active = deleteStatusVal
user.save()
return Response(UserSerializer(user).data)
except:
return Response("Nope")
serializers.py
class UserSerializer(ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'}, write_only=True)
email = serializers.EmailField(validators=[required])
class Meta:
model = User
fields = ['username', 'email', 'password', 'is_active']
extra_kwargs = {'password': {'write_only': True},
'is_active': {'read_only': True}}
def validate(self, data):
email = data.get('email', None)
user = User.objects.filter(email=email).distinct()
if user.exists():
raise ValidationError("That email is already registered!")
return data
class UserInformationUpdateSerializer(ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = UserInformation
fields = ['user', 'first_name', 'middle_name', 'last_name', 'phone', 'date_of_birth']
models.py
class UserInformation(BaseModel):
user = models.OneToOneField(User, related_name='user_id')
first_name = models.CharField(max_length=45)
middle_name = models.CharField(max_length=45, null=True)
last_name = models.CharField(max_length=45)
vendor = models.BooleanField(default=False)
phone = models.CharField(max_length=100, validators=[
RegexValidator(regex=r'^\+?8801?\d{9}$', message="Phone number must be entered in the format: '+8801*********'")
], blank=False, unique=True)
date_of_birth = models.DateField()
confirmation_token = models.CharField(max_length=45, null=True)
confirmation_exp = models.DateTimeField(null=True)
pw_reminder_token = models.CharField(max_length=45, null=True)
pw_reminder_exp = models.DateTimeField(null=True)
profile_pic = models.ImageField(blank=True, null=True, upload_to='profile_images/', default='Images/none/no_images.jpg')
cover_photo = models.ImageField(blank=True, null=True, upload_to='cover_images/', default='Images/none/no_images.jpg')
thumbnail_pic = models.ImageField(blank=True, null=True, upload_to='thumbnail_images/', default='Images/none/no_images.jpg')
phone_verified = models.BooleanField(default=False)
email_verified = models.BooleanField(default=False)
reward_points = models.IntegerField(null=False)
ref_code = models.CharField(null=True, max_length=10)
def __str__(self):
return self.user.username
def delete(self, *args, **kwargs):
self.user.delete()
super(UserInformation, self).delete(*args, **kwargs)
If you want to make User as in active while keeping the UserInformation object and Userobject un-deleted in database, you can do something like this:
def destroy(self, request, *args, **kwargs):
user = self.get_object().user
user.is_active = False
user.save()
return Response(UserInformationUpdateSerializer(self.get_object()).data)
You have 'is_active': {'read_only': True}}.
Also,
# this seems redundant
def delete(self, *args, **kwargs):
self.user.delete()
super(UserInformation, self).delete(*args, **kwargs)