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)
Related
I have a problem with select_related. I don't know what I'm doing wrong but it doesn't work..
models.py
class OrganizerUser(models.Model):
"""This user manage Agents"""
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Agent(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
organizer = models.ForeignKey(OrganizerUser, blank=True, null=True,
on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Lead(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
age = models.IntegerField(default=0)
organizer = models.ForeignKey(OrganizerUser, on_delete=models.CASCADE)
agent = models.ForeignKey(Agent, null=True, blank=True, on_delete=models.SET_NULL)
category = models.ForeignKey(
Category, related_name="categories", null=True, blank=True, on_delete=models.SET_NULL
)
description = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
phone_number = models.CharField(max_length=20)
email = models.EmailField()
converted_date = models.DateTimeField(null=True, blank=True)
def __str__(self):
return f"{self.first_name} {self.last_name}"
views.py
class LeadsApiView(generics.ListCreateAPIView):
serializer_class = LeadSerializer
permission_classes = [IsAuthenticated, IsAdminOrOrganizer]
def get_queryset(self):
user = self.request.user
#if user.is_staff:
#return Lead.objects.select_related('organizer', 'agent').all()
if user.is_organizer:
return Lead.objects.select_related('organizer').filter(
organizer=user.organizeruser)
else:
return Lead.objects.select_related('agent').filter(agent=user.agent)
serializers.py
class LeadSerializer(serializers.ModelSerializer):
class Meta:
model = Lead
fields = ['id', 'first_name', 'last_name', 'age',
'organizer', 'agent', 'category', 'description', 'date_added',
'phone_number', 'email', 'converted_date'
]
for agents everything is fine. Django makes 3 queries but for other users, it makes extra queries for each existing user.
This solution solved the problem
How to always prefetch_related for a specific django model
class UsersManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related('user')
class OrganizerUser(models.Model):
"""This user manage Agents"""
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
objects = UsersManager()
def __str__(self):
return self.user.username
class Agent(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
organizer = models.ForeignKey(OrganizerUser, blank=True, null=True,
on_delete=models.CASCADE)
objects = UsersManager()
def __str__(self):
return self.user.username
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
I want to extend my Django user model using rest_framework to create an api endpoint for project, but I'm getting a KeyError: user, below is my code.
profile apiView
class ProfileListView(generics.ListCreateAPIView):
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
profile serializer
class ProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source = 'pk', read_only = True)
username = serializers.CharField(source = 'user.username', read_only = True)
email = serializers.CharField(source = 'user.email', read_only=True)
class Meta:
model = Profile
fields = ('id', 'email', 'username', 'qualification', 'profession', 'phoneNumber',
'city', 'address', 'surname', 'firstName', 'dp', 'yearOfExperience',
'gender'
)
def create(self, validated_data, instance=None):
user_data = validated_data.pop('user')
user = CustomUser.objects._create_user(**validated_data)
Profile.objects.create(user=user, **user)
return user
profile model
class Profile(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
firstName = models.CharField(max_length=255, blank=True, null=True)
surname = models.CharField(max_length=255, blank=True, null=True)
yearOfExperience = models.PositiveIntegerField(default=1)
profession = models.CharField(max_length=250, blank=True, null=True)
dp = models.URLField(blank=True, null=True)
qualification = models.CharField(max_length=255, blank=True, null=True)
phoneNumber = models.CharField(max_length=255, blank=True, null=True)
city = models.CharField(max_length=255, blank=True, null=True)
address = models.CharField(max_length=255, blank=True, null=True)
gender = models.CharField(max_length=255, choices=GENDER)
class Meta:
ordering = ('surname', '-firstName', )
verbose_name = 'Profile'
verbose_name_plural = 'Profiles'
def __str__(self):
return self.user.username
#receiver(post_save, sender=CustomUser)
def create_user_profile(sender, instance=None, created=False, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
Error message:
File "/home/olaneat/Desktop/filez/project/django/funzone/lib/python3.7/site-packages
user_data = validated_data.pop('user')
KeyError: 'user'
You need to check if the user is there before accessing it;
def create(self, validated_data, instance=None):
if 'user' in validated_data:
user_data = validated_data.pop('user')
user = CustomUser.objects._create_user(**validated_data)
Profile.objects.create(user=user, **user)
return user
However you don't never need to do that validated_data.pop() because you're not using user_data after assigning it.
my record/model.py is,
class HistoricalRecords(models.Model):
user = models.ForeignKey('User', on_delete=models.CASCADE, null=True, blank=True)
role = models.CharField(max_length=255, null=True, blank=True)
model = models.CharField(max_length=255, null=True, blank=True)
torque = models.IntegerField(null=True, blank=True)
car = models.TextField(null=True, blank=True)
date_time = models.DateTimeField(default=timezone.now)
my record/serializer.py is
class SaveAuditRecordSerializer(serializers.ModelSerializer):
class Meta:
model = HistoricalRecords
fields = ('user', 'role', 'model', 'torque', 'car', 'date_time')
and my record/views.py is,
def AuditRecord(request):
serializer = SaveAuditRecordSerializer(data=request, partial=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_201_CREATED)
return Response(serializer.errors,
status=status.HTTP_201_CREATED)
I am trying to call this AuditRecord(req) from another view of another app, like
rec = {}
audit_record['user'] = request.user
audit_record['role'] = "Role"
audit_record['model'] = "M"
audit_record['torque'] = 22222
audit_record['action'] = "car created"
audit_record['date_time'] = datetime.now()
AuditRecord(audit_record)
I dont get any error but it is not getting saved to db
is there any mistake in my approch?
If there's a model in your other app that points to your AuditRecord and you wanna create the record on it's create enpoint, you can use a serializer of this other app to create an instance of AuditRecord.
OtherSerializer(serializers.Serializer):
audit_record = AuditRecordSerializer(required=False, write_only=True)
def create(self, validated_data):
audit_record_data = validated_data.pop('audit_record')
audit_record = AuditRecordSerializer().create(audit_record_data)
validated_data.update({'audit_record': audit_record})
return super(OtherSerializer, self).create(validated_data)
I have the following model
class Owner(models.Model):
user = models.OneToOneField(User, default=1, editable=True)
phone = models.CharField(max_length=40, null=True, blank=True)
address = models.CharField(max_length=255, null=True, blank=True)
city = models.CharField(max_length=255, null=True, blank=True)
state = USStateField(null=True, blank=True)
zip = models.CharField(max_length=20, null=True, blank=True)
def __str__(self):
return "%s %s" % (self.user.first_name, self.user.last_name)
class Device(CreationModificationMixin):
_STATUSES = (
('A', 'Active'),
('I', 'Inactive'),
('F', 'Failure'),
)
_TYPES = (
('S', 'Spa'),
('P', 'Pool'),
)
udid = models.CharField(max_length=255, verbose_name="Unique ID / MAC Address", null=False, blank=False, unique=True)
type = models.CharField(max_length=1, choices=_TYPES, null=False, blank=False)
title = models.CharField(max_length=255, null=False, blank=False)
status = models.CharField(max_length=1, default='A', choices=_STATUSES)
pinged = models.DateTimeField(null=True)
owner = models.ForeignKey(Owner, verbose_name="Owner", null=True, blank=True)
def __str__(self):
return self.udid
I have the following serializer
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = ('id', 'udid', 'title', 'type', 'status', 'pinged', 'created')
I have the following API View defined:
class DeviceAPIView(APIView):
permission_classes = (IsAuthenticated,) # explicit
code_404 = "Device doesn't exists"
def get(self, request, device_id):
try:
d = Device.objects.get(id=device_id, owner=request.user.owner)
except Device.DoesNotExist:
return Response({'error': self.code_404}, 404)
serializer = DeviceSerializer(d)
return Response(serializer.data)
def put(self, request, device_id):
serializer = DeviceSerializer(data=request.DATA)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
data = serializer.data
data['id'] = id
d = Device(**data).save()
serializer = DeviceSerializer(d)
return Response(serializer.data, status=status.HTTP_200_OK)
PUT request on existing device
{
"udid": "38-2C-4A-47-C2-ED",
"title": "Backyard pool",
"type": "S"
}
gives me back
{
"udid": ["This field must be unique."]
}
However I'm updating the record and passing the same UDID it has. So I'm not getting a duplicate in DB but DRF thinks the other way.
What I need to achieve is
If UDID of the same record is not changed - then no error should be raised
if UDID of the record changes and now it's the same as some of record's UDID then error should be returned.
As per the comments implementing the put method closer to the reference implementation in the docs should fix the issue.
def put(self, request, pk, format=None):
device = self.get_object(pk)
serializer = DeviceSerializer(device, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Saving instances has a bit more information on creating, updating and saving instances by using the serializer class methods.