Django : One to Many serializer attribute error - django

Hello i have one to many model serializer but when I try to make request on it it says attribute error.
Got AttributeError when attempting to get a value for field `project` on serializer `clientserializers`.
The serializer field might be named incorrectly and not match any attribute or key on the `Client` instance.
Original exception text was: 'Client' object has no attribute 'projectname'
This is how i execute on views
jsonified_client = client_serialize.clientserializers(client_list, many=True)
json_data = JSONRenderer().render(jsonified_client.data)
return HttpResponse(json_data, content_type='application/json')
My model :
# client structure
class Project(db.Model):
project_name = db.CharField(max_length=50, unique=True)
def __str__(self):
return self.project_name
class Reason(db.Model):
reason_name = db.CharField(max_length=50, unique=True)
def __str__(self):
return self.reason_name
class Client(db.Model):
name = db.CharField(max_length=100)
project = db.ForeignKey(Project, on_delete=db.CASCADE, related_name="projectname")
reason = db.ForeignKey(Reason, on_delete=db.CASCADE, related_name="reasonname")
timestamp = db.DateTimeField(default=timezone.now())
Serializer both for project and reason:
class projectserializers(serializers.ModelSerializer):
class Meta:
model = Project
fields =('id', 'project_name')
class reasonserializers(serializers.ModelSerializer):
class Meta:
model = Reason
fields =('id', 'reason_name')
class clientserializers(serializers.ModelSerializer):
project = projectserializers(source="projectname", many=True)
reason = reasonserializers(source="reasonname", many=True)
class Meta:
model = Client
fields = ('id', 'name', 'timestamp', 'project', 'reason')

Remove the source and many argument values -
class clientserializers(serializers.ModelSerializer):
project = projectserializers()
reason = reasonserializers()
class Meta:
model = Client
fields = ('id', 'name', 'timestamp', 'project', 'reason')
As per the PEP8 standards, class names should normally use the CapWords convention.
So, projectserializers should be Projectserializers (or ProjectSerializers) and reasonserializers should be Reasonserializers (or ReasonSerializers)

Related

Django rest framework; how do you use the ID of a foreign key to create an instance through the serializer?

I have two serializers, one for Country and one for my model Foo, I want to save the object by using the foreign key for this model but it errors out whenever I try to validate.
I have this
class Actor(TLPWrappedModel, CommentableModel):
label = models.CharField(max_length=56, unique=True)
country_of_origin = models.ForeignKey(Country, on_delete=models.CASCADE)
class FooSerializer(serializers.ModelSerializer):
country_of_origin = CountrySerializer()
class Meta:
model = Actor
fields = [
'id',
'country_of_origin',
'label',
]
class Country(models.Model):
label = models.CharField(max_length=56, unique=True)
iso_code = models.CharField(max_length=3, unique=True)
class CountrySerializer(serializers.ModelSerializer):
class Meta:
model = Country
fields = [
'iso_code',
'label',
]
And this is what I'm trying to do
serializers = FooSerializer(data={'label': 'Foobar',
'country_of_origin': self.country.id})
serializers.is_valid()
print(serializers.errors)
print(serializers.validated_data)
serializers.save()
But I get this error {'country_of_origin': {'non_field_errors': [ErrorDetail(string='Invalid data. Expected a dictionary, but got int.', code='invalid')]}}
is it possible to use the ID of a foreign key to validate and create the object using the serializer?
We can update the to_represent of the FooSerializer to get the desired output
Try
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Actor
fields = [
'id',
'country_of_origin',
'label',
]
def to_representation(self, instance):
data = super().to_representation(instance)
data['country_of_origin'] = CountrySerializer(instance.country_of_origin)
return data
serializers = FooSerializer(data={'label': 'Foobar', 'country_of_origin': self.country})
serializers.is_valid(raise_expection=True)
serializers.save()
In this I have updated the code to assign the self.country as country_of_origin. Also, I am using the raise_expection in the is_valid method. This method will return the errors as 400 response.
Try
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Actor
fields = [
'id',
'country_of_origin',
'label',
]
You can safely drop defining the 'country of origin` in the FooSerializer
contry_of_origin would be an object, and you are passing an id for it.
Do you need a nested serializer? : country_of_origin = CountrySerializer()
For the example that you have given, I would suggest you to change it to PrimaryKeyRelatedField()
Your serializer would look like:
class FooSerializer(serializers.ModelSerializer):
country_of_origin = serializers.PrimaryKeyRelatedField()
class Meta:
model = Actor
fields = [
'id',
'country_of_origin',
'label',
]

Got AttributeError when attempting to get a value for field `members` on serializer `HealthQuotationSerializer`

Trying out serialising parent and child model.Here are my models:
class HealthQuotation(models.Model):
quotation_no = models.CharField(max_length=50)
insuredpersons = models.IntegerField()
mobile_no = models.CharField(max_length=10)
def __str__(self):
return self.quotation_no
class HealthQuotationMember(models.Model):
premium = models.FloatField(null=True)
suminsured = models.FloatField()
quotation = models.ForeignKey(HealthQuotation,on_delete=models.CASCADE)
def __str__(self):
return str(self.quotation)
Here are my serializers:
class HealthQuotationMemberSerializer(serializers.ModelSerializer):
class Meta:
model = HealthQuotationMember
fields= "__all__"
class HealthQuotationSerializer(serializers.ModelSerializer):
members = HealthQuotationMemberSerializer(many=True)
class Meta:
model = HealthQuotation
fields = ['id','members']
On Serialising parent model with parent serializer, Django throws error "Got AttributeError when attempting to get a value for field members on serializer HealthQuotationSerializer. The serializer field might be named incorrectly and not match any attribute or key on the HealthQuotation instance. Original exception text was: 'HealthQuotation' object has no attribute".
Because you don't have members field in your model... Try to change your serializer as following and see if it works:
class HealthQuotationSerializer(serializers.ModelSerializer):
quotation = HealthQuotationMemberSerializer()
class Meta:
model = HealthQuotation
fields = ['id','quotation']
Note that I've removed many=True because there will be always one object per this data (ForeignKey). when you have more than one object such as Many2Many you should use many=True.
You have "HealthQuotation" as a parent and "HealthQuotationMember" as a child.
Now, you have decided to retrieve data from parent "HealthQuotation"
and its associated children which will come from "HealthQuotationMember", right?
To achieve that you can use Django SerializerMethodField():
Your serializers.py should look like:
class HealthQuotationMemberSerializer(serializers.ModelSerializer):
class Meta:
model = HealthQuotationMember
fields= '__all__'
class HealthQuotationSerializer(serializers.ModelSerializer):
members = serializers.SerializerMethodField() # I am using SerializerMethodField()
class Meta:
model = HealthQuotation
fields = '__all__'
def get_members(self, quotation):
q = HealthQuotationMember.objects.filter(quotation = quotation)
serializer = HealthQuotationMemberSerializer(q, many=True)
return serializer.data
Your views.py
class GetHealthQuotationList(ListAPIView):
serializer_class = HealthQuotationSerializer
queryset = HealthQuotation.objects.all()
Your url.py should be:
path('get-health-quotation-list', GetHealthQuotationList.as_view()),
NOTE: In case you plan to retrieve data from child table and find its associated parent, then your serializer should be good to go without many=True argument.

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')

Dynamically create serializer based on model field value

I have a model like so:
class A:
name = models.CharField()
group = models.ForeignKey('SomeModel', null=True, blank=True)
When I serialize this, I would like the serielizer to have different formats based on whether the 'group' field is blank or not. Of course this can be achieved by having different serializers for different formats and calling them as required in the View layer:
class TypeASerializer(serializers.ModelSerializer)
class Meta:
model = A
fields = ('id', 'name')
class TypeBSerializer(serializers.ModelSerializer)
class Meta:
model = A
fields = ('id', 'name', 'group')
But I wanted to handle it in the serializer layer itself and have a single serializer for this. Is that possible?
Serializer.instance may be None in some cases.
And get_fields() is called only once because Serializer.fields is cached from django-rest-framework 3.10: https://github.com/encode/django-rest-framework/commit/7232586c7caf66f20f56b36f1c6a9c9648eb94a4
In other words, when a serializer is used as a list by many=True (in ListModelMixin, or as a field of another serializer), the fields of all items in the list are determined by the first instance.
In that case, the solution is to override to_representation():
class TypeASerializer(serializers.ModelSerializer)
class Meta:
model = A
fields = ('id', 'name', 'group')
def to_representation(self, instance):
ret = super().to_representation(instance)
if not instance.group:
del ret['group']
return ret
This solution is a little inefficient because all fields and values are obtained from super().to_presentation() but some of them are removed again. You can consider fully implementing to_representation() without calling super's.
you can override the get_fields methods of serializer
class YourSerializer(serializers.ModelSerializer):
id = serializers.SerializerMethodField()
name = serializers.SerializerMethodField()
group = serializers.SerializerMethodField()
class Meta:
model = A
fields = ('id', 'name', 'group')
def get_fields(self):
fields = super().get_fields()
# delete all the unnecessary fields according to your logic.
if self.instance.group: # if this is detials view other wise pass object in context
del fields['group']
return fields
You can declare every field of your serializer as SerializerMethodField as follows:
class YourSerializer(serializers.ModelSerializer):
id = serializers.SerializerMethodField()
name = serializers.SerializerMethodField()
group = serializers.SerializerMethodField()
class Meta:
model = A
fields = ('id', 'name', 'group')
def id(self, obj):
if yourcondition(obj.group):
return obj.id
return another_value
...

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.