Django update_or_create raises IntegrityError within Serializer - django

I have the following ModelSerializer with a create method. In this method I call the model's update_or_create method. But when I do this, the serializer's validation raises the error
rest_framework.exceptions.ValidationError: [{'non_field_errors': [ErrorDetail(string='The fields user_id, capacity_id must make a unique set.', code='unique')]}, {}].
I thought that since I'm using update_or_create, it would find the row that matches validated data's user_id and capacity_id, and then update that row. But the validation runs before create, and the data is not valid because of the unique constraint. So how do I ignore this constraint?
class ActivatedCapacitySerializer(serializers.ModelSerializer):
user_id = serializers.IntegerField(required=False)
capacity_id = serializers.IntegerField(required=False)
class Meta:
model = ActivatedCapacity
fields = ('user_id', 'capacity_id', 'active')
def create(self, validated_data):
activated_capacity = ActivatedCapacity.objects.update_or_create(
user_id=validated_data['user_id'],
capacity_id=validated_data['capacity_id'],
defaults = {
'active': validated_data['active']
}
)
return activated_capacity
Models.py
class ActivatedCapacity(models.Model):
user_id = models.IntegerField()
capacity_id = models.IntegerField()
active = models.BooleanField(default=False)
class Meta:
unique_together = ('user_id', 'capacity_id',)

I just had to include in the serializer's class Meta an empty validators list, so it will override the model's default validators.
class ActivatedCapacitySerializer(serializers.ModelSerializer):
user_id = serializers.IntegerField(required=False)
capacity_id = serializers.IntegerField(required=False)
class Meta:
model = ActivatedCapacity
fields = ('user_id', 'capacity_id', 'active')
validators = []
...

Related

Multiple slug_field in SlugRelatedField Django Rest Framework

In my Django application I am getting Json like this:
"sales_order": 102,
"transport_by": 4,
I want to expand the sales_order and replace it with it's owner's first_name + last_name.
So I tried using slugrelated field but I am not sure how to get two values out of it.
Here's what I tried:
class AtableSOSerializer(serializers.ModelSerializer):
owner = serializers.SlugRelatedField(read_only=True, slug_field='first_name'+' '+'last_name')
class Meta:
model = MaterialRequest
fields = "__all__"
class AtableFlowListSerializer(serializers.ModelSerializer):
class Meta:
model = AllotmentFlow
fields = "__all__"
class AllotmentTableSerializer(serializers.ModelSerializer):
flows = AtableFlowListSerializer(many=True)
sales_order = AtableSOSerializer(read_only=True)
class Meta:
model = Allotment
fields = "__all__"
But obvious error appeared:
AttributeError: 'User' object has no attribute 'first_name last_name'
How do I get the first_name + last_name in my JSON?
i had same problem as you :
in models.py add a proprety to your model then in your serializer make your slug_field is the proprety you created in your model.
#property
def full_name(self):
return self.first_name+" "+self.last_name
owner = serializers.SlugRelatedField(read_only=True, slug_field='full_name')

Django Rest Framwork - Select only some fields in reverse lookup queryset

I'm trying to retrieve only some of the fields in the "Appointments" associated to a rental property "Unit". From the UnitSerializer, I call a SerializerMethodField() to do a reverse lookup for the "appointment" field. This works out well. However, the queryset returns all the fields (id, time, unit, staff, prospect) in each object, when I only need a few (id, time).
I tried .values() on the queryset like so:
queryset = instance.appointment_set.values('id', 'appointment_time')
But I get "Got KeyError when attempting to get a value for field unit on serializer AppointmentSerializer.\nThe serializer field might be named incorrectly and not match any attribute or key on the dict instance.\nOriginal exception text was: unit."
Note sure if you need all the code, but here's the essential.
Models
class Appointment(models.Model):
appointment_time = models.DateTimeField()
unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
staff = models.ForeignKey(Staff, on_delete=models.CASCADE)
prospect = models.ForeignKey(Prospect, on_delete=models.CASCADE)
Serializers
class AppointmentSerializer(serializers.ModelSerializer):
class Meta:
model = Appointment
fields = ['id','appointment_time']
class UnitSerializer(serializers.ModelSerializer):
appointment = SerializerMethodField()
class Meta:
model = Unit
fields = ['id', 'address', 'appointment']
def get_appointment(self, instance):
cutoff = _datetime.date.today() + timedelta(hours=72)
queryset = instance.appointment_set.exclude(appointment_time__gt=cutoff)
return AppointmentSerializer(queryset, many=True).data
There is a better way to handle reverse relationship in serializer:
class UnitSerializer(ModelSerializer):
appointment = AppointmentSerializer(many=True, source='appointment_set')
class Meta:
model = Unit
fields = ['id', 'address', 'appointment']

Validate field not in model by Django rest

I have a model:
class EventTracker(models.Model):
"""
Track events of user's behaviors
"""
class Meta:
verbose_name = "EventTracker"
verbose_name_plural = "EventTrackers"
unique_together = ("application", "label")
application = models.ForeignKey(Application, related_name='events')
label = models.CharField(max_length=50)
count = models.PositiveIntegerField(default=0)
value = models.IntegerField(null=True)
def __str__(self):
return "[{}] {}".format(self.application, self.label)
This is my serializer for this model:
class EventTrackerSerializer(serializers.ModelSerializer):
subscriber_id = serializers.IntegerField(min_value=1)
class Meta:
model = EventTracker
fields = ('id', 'application', 'label', 'count', 'value', 'subscriber_id')
write_only_fields = ('subscriber_id', )
read_only_fields = ('count',)
subscriber_id is a field that doesn't belong to this model. But request data must have subscriber_id to do a thing. So I want to validate it in serializer. I don't know how to validate it. I tried like above, it threw error:
This may be because you have a writable field on the serializer class that is not a valid argument to.....
So what can I do ?
First, you should probably be more explicit about what you want to do. We don't know what that field is for nor if it's readable nether what/how you want to validate it, so I'd do some guesswork.
Assuming it's write only:
subscriber_id = serializers.IntegerField(min_value=1, write_only=True)
Note that the write_only_fields has been removed for some time.
Next, you'll have to write manually the serializer's create/update. Example for the create:
class EventTrackerSerializer(serializers.ModelSerializer):
...
def create(self, validated_data):
subscriber_id = validated_data.pop('subscriber_id')
instance = EventTracker.objects.create(**validated_data)
return instance

django rest framework : nested model get not working. 'str' object has no attribute 'values'

I have a customer model in Bcustomer app that extends the django User model, So I will save the basic details such as name in User table and the remaining data (city, etc) in customer table.
Saving is working perfectly. But now it is showing the following error when I call the GET method.
AttributeError at /api/v1/customer 'str' object has no attribute 'values'
Request Method: GET
bcustomer/models.py
class BCustomer(models.Model):
customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True, blank=True )
address = models.CharField(max_length=50)
city = models.CharField(max_length=256)
state = models.CharField(max_length=50)
user = models.ForeignKey(settings.AUTH_USER_MODEL, db_index=True, on_delete=models.CASCADE, related_name='customer_creator')
# more fields to go
def __str__(self):
# return str(self.name) (This should print first and last name in User model)
class Meta:
app_label = 'bcustomer'
bcusomer/serializers.py
class CustomerDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = BCustomer
fields = ('city', 'phone')
class CustomerSerializer(serializers.ModelSerializer):
customer_details = CustomerDetailsSerializer()
class Meta:
model = get_user_model()
fields = ('id','first_name', 'email', 'customer_details')
def create(self, validated_data):
request = self.context.get('request')
customer_details_data = validated_data.pop('customer_details')
customer_user = get_user_model().objects.create(**validated_data)
BCustomer.objects.create(customer=customer_user, user=request.user, **customer_details_data)
customer_user.customer_details = customer_details_data
return customer_user
class CustomerListSerializer(serializers.ModelSerializer):
model = get_user_model()
fields = '__all__'
class Meta:
model = get_user_model()
fields = '__all__'
bcustomer/views.py
class CustomerViewSet(viewsets.ModelViewSet):
customer_photo_thumb = BCustomer.get_thumbnail_url
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
queryset = BCustomer.objects.all()
serializer_class = CustomerSerializer
def get_queryset(self):
queryset = BCustomer.objects.all()
return queryset
def get_serializer_class(self):
if self.action == 'list' or self.action == 'retrieve':
return CustomerListSerializer
return CustomerSerializer
bcustomer/urls.py
router.register(r'customer', views.CustomerViewSet, 'customers')
Data post parameter format
{
"first_name":"Myname",
"email":"testemail#gmail.com",
"customer_details": {
"city":"citys",
"phone":"04722874567",
}
}
You should remove model and fields from CustomListSerializer
class CustomerListSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = '__all__'
customer_details = CustomerDetailsSerializer()
You need to set the source argument to point to the user model's customer. Most probably:
customer_details = CustomerDetailsSerializer(source='customer')
(or maybe source='bcustomer', not sure if it reversed the field name or class name).
On a side not, you should not need the ListSerializer at all. The list method will call the serializer with the many=True argument on CustomerSerializer which will create the ListSerializer appropriately.

How to use Primary Key In a Serializer in Django

My Model is
class UserInfo(models.Model):
user = models.OneToOneField(User, unique=True)
mobile_no = models.CharField(max_length=10, blank=True)
and serialzer is :
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = UserInfo
fields = ('mobile_no','user')
but whenever I tried to use this
serializer = UserInfoSerializer(data=data)
if serializer.is_valid():
serializer.save()
It is not saving the data and giving errors.
Is there any method to use other then this to for using Primary key.
You should use PrimaryKeyRelatedField
add this to your serializer
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
Your UserInfoSerializer should look like:
class UserInfoSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
class Meta:
model = UserInfo
fields = ('mobile_no','user')
Update
If you want to update existing object in database then you have to pass model instance as an argument to UserInfoSerializer constructor.
user_info = self.get_object()
serializer = UserInfoSerializer(user_info, data=data)