Can't find a solution to this error please help or show me how to fix this.
The error is showing as follows:
(1048, "Column 'targetDefn_id' cannot be null") this error is showing when I post data.
This is my views.py in Post method I m getting this error:
def setTarget(request):
if request.method == 'POST':
data=JSONParser().parse(request)
print(data)
serial=TargetSerializers(data=data)
print(serial)
if serial.is_valid():
serial.save()
print("done")
return JsonResponse(serial.data,status=status.HTTP_200_OK,safe=False)
return JsonResponse(serial.errors, status=status.HTTP_400_BAD_REQUEST)
This is my Serializer.py as follows:
class TargetSerializers(serializers.ModelSerializer):
targetDefn=serializers.SerializerMethodField()
roleId=serializers.SerializerMethodField()
empId=serializers.SerializerMethodField()
class Meta:
model = Target
fields = (
'id',
'targetDefn',
'roleId',
'empId',
'startDate',
'endDate',
'value'
)
def get_targetDefn(self,obj):
trgt = TargetDefination.objects.get(id=obj.targetDefn_id)
serial = TargetDefinationSerializers(trgt)
return serial.data
def get_empId(self,obj):
emp= Employee.objects.get(id=obj.empId_id)
serial= OnlyEmployeeSerializers(emp)
return serial.data
def get_roleId(self,obj):
role=Role.objects.get(id=obj.roleId_id)
serial=RoleSerializers(role)
return serial.data
This is models.py as follows:
class Target(models.Model):
targetDefn=models.ForeignKey(TargetDefination,on_delete=models.CASCADE)
roleId=models.ForeignKey(Role,on_delete=models.CASCADE)
empId=models.ForeignKey(Employee,on_delete=models.CASCADE)
startDate= models.DateField(default=datetime.date.today)
endDate= models.DateField(null=True,blank=True)
value=models.PositiveIntegerField(default=0)
def __str__(self):
return str(self.empId) + ' ' +str(self.targetDefn)
Updated:
My Post query:
{
"targetDefn": {
"id": 1,
"targetName": "MIN SALES",
"displayName": "MIN SALES"
},
"roleId": {
"id": 3,
"roleName": "CIO",
"description": "chief information officer",
"roleReportsTo": 5,
"roleReportsToName": "SeniorVP(M)"
},
"empId": {
"id": 2,
"empName": "Emp02",
"startDate": "2021-04-01",
"termDate": null
},
"startDate": "2021-05-11",
"endDate": "2021-05-20",
"value": "123"
}
Know that the SerializerMethodField is READ-ONLY. It means that the data of those fields will not be written in the database. They will be computed and sent to your user, but never written in the DB. So, when you're saving your serializer, those fields are ignored.
I've noticed that you've created SerializerMethodField for all your foreign keys, only for their get_[field] method to return literally the serialized object. So why not simply use their nested-serializer right away?
class TargetSerializers(serializers.ModelSerializer):
targetDefn=TargetDefinationSerializers()
roleId=OnlyEmployeeSerializers()
empId=serializers.SerializerMethodField()
class Meta:
model = Target
fields = (
'id',
'targetDefn',
'roleId',
'empId',
'startDate',
'endDate',
'value'
)
If you cannot get it to work or wish to keep your SerializerMethodField structure, then we must add the data of those 3 fields before saving. You can either pass the data as parameter in the serializer, like so:
serializer.save(targetDefn=variable_1, roleId=variable_2, empId=variable_3)
Or you can override the create and update serializer methods to do it there, example:
def create(self, validated_data):
targetDefn = validated_data["targetDefn"]
roleId = validated_data["roleId"]
emp_id = validated_data["emp_id"]
# preprocess those data if needed
# Then create your model
Target.objects.create(targetDefn=targetDefn, ...)
Related
I'm using Django serializers to create an API which I both read from and write to.
Models:
class Biomarker(models.Model):
low_value_description = models.TextField(blank=True, null=True)
high_value_description = models.TextField(blank=True, null=True)
class BiomarkerReading(models.Model):
biomarker = models.ForeignKey(Biomarker, on_delete=models.CASCADE)
test = models.ForeignKey(Test, on_delete=models.CASCADE)
value = models.DecimalField(max_digits=30, decimal_places=8, default=0)
Serializer:
class BiomarkerReadingSerializer(serializers.ModelSerializer):
class Meta:
model = BiomarkerReading
fields = (
'id', 'test', 'biomarker', 'value'
)
JSON format:
{
"id": 617188,
"test" 71829,
"biomarker": 32,
"value": 0.001
}
The above all works, and I can read and write to it with that JSON format. However I now need to add some fields from the parent model so the response looks like this:
{
"id": 617188,
"test" 71829,
"biomarker": {
"id": 32,
"low_value_description": "All good",
"high_value_description": "You will die",
},
"value": 0.001
}
I have got the read part working using these Serializers:
class BiomarkerDescriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Biomarker
fields = ('id', 'low_value_description', 'high_value_description')
class BiomarkerReadingSerializer(serializers.ModelSerializer):
biomarker = BiomarkerDescriptionSerializer()
class Meta:
model = BiomarkerReading
fields = (
'id', 'test', 'biomarker', 'value'
)
However I can't find a way to write to it using the old json format (with "biomarker": 32 in the JSON).
I thought I would need to do something in validate or create, but I get a 400 response before it even hits those methods:
class BiomarkerReadingSerializer(serializers.ModelSerializer):
... # as above
def validate(self, data):
print('validate') # Doesn't print
data = super().validate(data)
return data
def create(self, validated_data):
print('create') # Doesn't print
return super().create(validated_data)
The example in the docs for writable-nested-serializers and the other examples I've found on SO only discuss the case of creating child records while writing to the parent record serializer, not the other way around.
I do not want to create a parent Biomarker via the API, I just need to be able to reference it by pk/id in the incoming JSON like before.
I don't mind if I have to change the names of keys to something like this for incoming:
{
"id": 617188,
"test" 71829,
"biomarker_id": 32,
"value": 0.001
}
Or something like this for the response:
{
"id": 617188,
"test" 71829,
"biomarker": 32,
"descriptions": {
"low_value_description": "All good",
"high_value_description": "You will die",
},
"value": 0.001
}
If that makes it easier.
I hate answering my own question, but the solution was to subclass serializers.RelatedField, which is explained in advanced-serializer-usage).
This lets you separately control how the value(instance) is represented as serialised, and how the serialised data is used to retrieve or create a value (instance).
class BiomarkerDescriptionSerializer(serializers.RelatedField):
def to_representation(self, value):
data = {}
for field in ('id', 'low_value_description', 'high_value_description'):
data[field] = getattr(value, field)
return data
def to_internal_value(self, data):
return Biomarker.objects.get(id=data)
def get_queryset(self, *args):
pass
class BiomarkerReadingSerializer(serializers.ModelSerializer):
biomarker = BiomarkerDescriptionSerializer()
...
The overridden get_queryset is required, even though it does nothing.
This means data going in looks like this:
{
"id": 617188,
"test" 71829,
"biomarker": 32,
"value": 0.001
}
Yet the data going out looks like this:
{
"id": 617188,
"test" 71829,
"biomarker": {
"id": 32,
"low_value_description": "All good",
"high_value_description": "You will die",
},
"value": 0.001
}
Thank you to those who offered answers, much appreciated
Using depth = 1 option will solve your problem.
class BiomarkerReadingSerializer(serializers.ModelSerializer):
class Meta:
model = BiomarkerReading
fields = (
'id', 'test', 'biomarker', 'value'
)
depth = 1
Take a look: https://www.django-rest-framework.org/api-guide/serializers/#specifying-nested-serialization
I have this Event model:
# models.py
class Event(models.Model):
name = models.CharField(max_length=200))
user = models.ManyToManyField(User))
This is my ListCreateAPIView class:
class EventListView(LoginRequiredMixin, generic.ListView):
model = Event
This is my ListCreateAPIView class:
class Events(generics.ListCreateAPIView):
permission_classes = (IsAuthenticated,)
queryset = Event.objects.all()
serializer_class = EventSerializer
This is my serialiser class:
#serializers,py
class EventSerializer(serializers.ModelSerializer):
class Meta:
fields = (
‘id',
'name',
'user',
)
model = Event
And this is my REST request response:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "My event1",
"user": [
1,
4
],
"registered": true // <—- I NEED THIS
},
{
"id": 2,
"name": "Other event",
"user": [
2,
4,
6
],
"registered": false // <—- I NEED THIS
}
]
}
What I am gonna need in REST response is property “registered”, which will contain true or false if current user is registered on particular event or not.
My approach was to control if current user is in list of users of event. I tried to get this information in serialiser class this way:
#serializers,py
class EventSerializer(serializers.ModelSerializer):
registered = serializers.SerializerMethodField(‘_user’)
def _user(self, obj):
request = getattr(self.context, 'request', None)
if request.user in self.user:
return True
else:
return False
class Meta:
fields = (
‘id',
'name',
'user',
)
model = Event
But I get AttributeError: 'NoneType' object has no attribute 'user'
How can I solve this problem?
You can try like this using exists() with filter method:
class EventSerializer(serializers.ModelSerializer):
registered = serializers.SerializerMethodField(‘_user’)
def _user(self, obj):
request = self.context.get('request', None)
if request and obj.user.filter(pk=request.user.pk).exists():
return True
else:
return False
In view.py add
For This "registered": true // <—- I NEED THIS
model_name.object.filter(column='required value')
Let's say I have a model:
class Entry(models.Model):
content = models.CharField(max_length=140)
email = models.EmailField()
upload_date = models.DateTimeField(default=timezone.now)
class Meta:
db_table = 'entries'
What I want to achieve is the viewset to return list of content and upload_date values per single email (since many entries can have the same email value), like that:
[
{
"email": "address1#test.test",
"entries": [
{
"upload_date": "2020-09-03",
"content": "test content 1"
},
{
"upload_date": "2020-09-02",
"content": "test content 2"
},
...
]
},
{
"email": "address2#test.test",
"entries": [
{
"upload_date": "2020-09-03",
"content": "test content 11"
},
{
"upload_date": "2020-09-02",
"content": "test content 12"
},
...
]
},
...
]
I tried messing around with .values() and .annotate(), but with no luck.
Quick answer with groupby itertools function :
def entry_converter(entry):
return {'upload_date': entry.upload_date, 'content': entry.content}
def grouping_key(entry):
return entry.email
from itertools import groupby
entries = Entry.objects.order_by('email')
grouped_entries = [dict(email=key, entries=list(map(entry_converter, group))) for key, group in groupby(entries, key=grouping_key)]
Update 1
Integration with DRF ViewSet
def entry_converter(entry):
return {'upload_date': entry.id, 'content': entry.id}
def grouping_key(entry):
return entry.email
def group_entries(entries):
return [
dict(email=key, entries=list(map(entry_converter, group))) for key, group in groupby(entries, key=grouping_key)
]
class EntrySerializer(ModelSerializer):
class Meta:
model = Entry
fields = '__all__'
class EntryViewSet(ModelViewSet):
queryset = Entry.objects.all()
serializer_class = EntrySerializer
def list(self, request, *args, **kwargs):
queryset = self.get_queryset().order_by('email')
data = group_entries(queryset)
page = self.paginate_queryset(data)
if page is not None:
return self.get_paginated_response(page)
return Response(data)
Keep in mind that I haven't used .filter_queryset() method of ModelViewSet as it may modify the queryset by adding wrong ordering or wrong filtering. Because groupby function needs sorted data as an input. Also, I have not overridden ModelSerializer class for generalizing .list() method of ModelViewSet as it may create extra complexity.
I have two serializers
class CheckItemSerializer(serializers.ModelSerializer):
class Meta:
model = CheckItem
fields = (
'item_name',
'amount',
)
class PaymentActionSerializer(serializers.ModelSerializer):
items = CheckItemSerializer(many=True, required=False)
class Meta:
model = PaymentAction
fields = [
'booking_number',
'date',
'guest_name',
'isRefund',
'lcode',
'payment_type',
'positions',
'total',
'items',
'id'
]
def create(self, validated_data):
action = PaymentAction.objects.create(**validated_data)
action.save()
if validated_data.get('items', None) is not None:
items = validated_data.pop('items')
if items is not None:
for item in items:
item_name = item['item_name']
amount = item['amount']
new_item = CheckItem.objects.create(
item_name=item_name,
amount=amount
)
new_item.save()
action.items.add(new_item)
action.save()
return action
and json
{"lcode": 123,
"total": 1,
"isRefund": false,
"booking_number": "333",
"guest_name": "me",
"positions": "1 night",
"date": "2019-07-22 00:00",
"payment_type": "nal",
"items": [
{
"item_name": "glazka",
"amount": "100"
},
{
"item_name": "glazka2",
"amount": "150"
}
]
}
and I get error
"<PaymentAction: PaymentAction object>" needs to have a value for field "id" before this many-to-many relationship can be used.
What am I doing wrong ?
You passed the items parameter to your PaymentAction object as well, but since at that point, your PaymentAction has not (yet) a primary key, it can not add these items to a many-to-many field.
You thus should pop that from the validated_data first:
def create(self, validated_data):
items = validated_data.pop('items', None)
action = PaymentAction.objects.create(**validated_data)
if items is not None:
items = [CheckItem.objects.create(**item) for item in items]
action.items.add(*items)
return action
I was bit confused about having custom structure with custom field, attached my reference implementation. If you see do_representation you output was not as expected, anyone know why is it so?
class ImageSerializer(serializers.ModelSerializer):
thumb = serializers.ImageField()
class Meta:
model = Image
fields = ('thumb',)
class ProductSerializer(serializers.ModelSerializer):
def product_url(self,obj):
request = self.context['request']
return request.build_absolute_uri(reverse('product', args=(obj.slug,)))
url = serializers.SerializerMethodField('product_url')
images = ImageSerializer(many=True)
class Meta:
model = Product
fields = ('url', 'images', 'id')
def to_representation(self,product):
raise Exception(product.url) # Not working
raise Exception(product.images) # Not working
raise Exception(product.id) # Working
Here is the error message
'Product' object has no attribute 'url'
Note: But if I don't override with to_representation then json response has url field
My workaround
Models
class Product(models.Model):
title = models.CharField(max_length=256,null=True,blank=False)
class ProductImage(models.Model):
product = models.ForeignKey('Product',on_delete=models.CASCADE,null=True,blank=True,related_name='product_images')
image = models.ImageField(upload_to='product/',null=True)
thumb = ImageSpecField(source='image',
processors=[ResizeToFill(100, 1100)],
format='JPEG',
options={'quality': 70})
Actual Output
{
"count":5,
"next":"http://localhost:8000/api/products/?format=json&page=2",
"previous":null,
"results":[
{
"id":1,
"images":[
{
"thumb":"http://localhost:8000/media/CACHE/images/product/Product1/fee2eb25a1d7b954632dd377aca39995.jpg"
},
{
"thumb":"http://localhost:8000/media/CACHE/images/product/Product2/a279c5057bb5ee6e06945f98d89cc411.jpg"
}
],
"url":"http://localhost:8000/product/wooden-furniture/"
}
]
}
Expected Output
{
"count":5,
"next":"http://localhost:8000/api/products/?format=json&page=2",
"previous":null,
"results":[
{
"id":1,
"images":[
"thumb":"http://localhost:8000/media/CACHE/images/product/Product1/fee2eb25a1d7b954632dd377aca39995.jpg",
"popup":"http://localhost:8000/media/CACHE/images/product/Product2/a279c5057bb5ee6e06945f98d89cc411.jpg" # Will work on it once I achieve it wil popup
],
"url":"http://localhost:8000/product/wooden-furniture/"
}
]
}
My custom structure tryout
def to_representation(self,obj):
return {
'id': obj.id,
'images': {
'thumb': obj.images # May be I have to work to get only first image here
},
'url': obj.url
}
As you are respresnting the data by your own. So you have to do other things by own.
Try this,
class ProductSerializer(serializers.ModelSerializer):
def product_url(self,obj):
request = self.context['request']
return request.build_absolute_uri(reverse('product', args=(obj.slug,)))
class Meta:
model = Product
fields = ('url', 'images', 'id')
def to_representation(self, product):
image_objs = ProductImage.objects.filter(product=product)
images = ImageSerializer(image_objs, many=True)
return {
"id": product.id,
"images" images.data,
"url": self.product_url(product)
}