Return row where column is NULL - django-rest-framework

I've created an API with rest_framework and can receive API calls that updates the database, but I can't figure how to query the database and return any row where the field "Endtime" is NULL.
In the function below I'm updating the database with the received JSON-data and this fails to return any result where the value is NULL for Endtime. Below is the error I get. How should I write the view to return rows where endtime column is NULL?
ValidationError at /durationupdate/
['“NULL” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.']
Request Method: POST
Request URL: http://127.0.0.1:8000/durationupdate/
Django Version: 4.1.1
Exception Type: ValidationError
Exception Value:
['“NULL” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.']
views.py
`
#api_view(['POST', 'GET'])
def durationUpdate(request):
if request.method == 'POST':
serializer = RigstateSerializer(data=request.data)
wherenull = Rigstate.objects.get(endtime='NULL')
wherenullserializer = DurationSerializer(wherenull, many=True)
if serializer.is_valid():
serializer.save()
return Response(wherenullserializer.data)
`
models.py
`
class Rigstate(models.Model):
rigname = models.CharField(max_length=255)
rigmode = models.IntegerField(default=0)
starttime = models.DateTimeField()
endtime = models.DateTimeField(blank=True, null=True)
duration = models.IntegerField(default=0)
def __str__(self):
return self.rigname
`
I've tried changing from 'NULL' to NULL but then I get a different error

You should your endtime write only and override to_represenation method like this.
class RigstateSerializer(serializer.ModelSerializer):
class Meta:
model = Rigstate
fields = '__all__'
extra_kwargs = {'endtime': {'write_only': True}}
def to_representation(self, instance):
data = super().to_representation(instance)
if instance.endtime:
data['endtime'] = instance.endtime.strftime(#write you required format here)
return data

Changed name to a more appropriate (DurationList)
views.py
#api_view(['GET'])
def durationList(request):
if request.method == 'GET':
duration = Rigstate.objects.values_list('endtime')
wherenullserializer = DurationSerializer(duration, many=True)
return Response(wherenullserializer.data)
Most changes were in serializers.py
class DurationSerializer(serializers.ModelSerializer):
class Meta:
model = Rigstate
endtime = serializers.DateTimeField(allow_null=True)
fields = ('endtime',)
Now I only get columns where entime is null, and only the endtime value

Related

not a valid choice in django restframework serializer

i set my post model with choice and migrate model.
after that i create postserializer for create
and i run the server and post with data in postman for test,
but i got the 'is not a valid choice' err on serializer.
here is my model,
class Post(models.Model):
CATEGORY_CHOICES = (
('mc', 'MIRACLE'),
('hw', 'HOMEWORK')
)
title = models.CharField(max_length=50)
category = models.CharField(max_length=2, choices=CATEGORY_CHOICES)
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
serializer,
class PostCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
view
class PostAPI(APIView):
def post(self, request):
data = {
'title' :request.data['title'],
'category':request.data['category'],
'content' :request.data['content'],
'author' :request.user.id
}
serializer = PostCreateSerializer(data=data)
if serializer.is_valid():
serializer.save()
else:
print(serializer.erros)
...
and request data (in Postman)
{
'title':'test_title',
'category':'HOMEWORK',
'content':'test_content'
}
result is
{'category': [ErrorDetail(string='"HOMEWORK" is not a valid choice.', code='invalid_choice')]}
i tried request with changing category 'HOMEWORK' to 'hw'
then it works
but i want request with large one
Your error on serialize level. Try this
readable_to_choice = {"HOMEWORK": "hw", "MIRACLE": "mc"}
data = {'title' :request.data['title'],
'category': readable_to_choice.get(request.data['category']),
'content' :request.data['content'],
'author' :request.user.id}
serializer = PostCreateSerializer(data=data)
UPD: you should make your api func like this:
serializer = PostCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
print(serializer.data) # do logic with that data
and try to override to_internal_value method in your serializer. for example:
def to_internal_value(self, data):
readable_to_choice = {"HOMEWORK": "hw", "MIRACLE": "mc"}
data["category"] = readable_to_choice.get(data["category"])
res = super().to_internal_value(data)
return res
I think this way is more accurate

I have to create serializer instance without data since i am not accepting any data from user

Here i am trying to create serializer instance without data argument because all i want to create a "Like" object which requires "user" object which i can get from request and "post" object that i am getting through querying Post model with pk but since i am not passing any data argument while deserializing it will throw an error.
So how can create instance without passing data argument or do i have to change my code and add data argument?
class Like(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="likes")
liked_by = models.ForeignKey(User, on_delete=models.RESTRICT, related_name = "liked_posts")
def __str__(self):
return "{0} liked by {1}".format(self.post.img, self.liked_by.username)
class LikeSerializer(serializers.ModelSerializer):
liked_by = UserSerializer(read_only=True)
class Meta:
model = Like
fields = ("id","post", "liked_by")
class AddLike(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, pk):
post = Post.objects.get(pk=pk)
user = request.user
serializer = LikeSerializer()
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.save(post=post, liked_by=user)
return Response(data= serializer.data, status=status.HTTP_201_CREATED)
I think you can set the post field as read_only in the LikeSerializer.
class LikeSerializer(serializers.ModelSerializer):
liked_by = UserSerializer(read_only=True)
class Meta:
model = Like
fields = ("id","post", "liked_by")
extra_kwargs = {
'post': { 'read_only': True }
}

Unable to save with two models id field

Groom can like Bride, and Bride can like Groom. These two are different models in Likes Model.
I'm using the view set. Able to save individual model data but when I save Likes DRF throws field required error. Please suggest how to do better handling of many to many relationship handling between two models.
I also tried with ForeignKey relation in the models. The from bride/groom would be enforced with logged-in user id that is register bride/groom id (pk).
Models
class RegisterBride(models.Model):
'''Register Bride/Female profiles.'''
fname = models.CharField(max_length=200)
lname = models.CharField(max_length=200)
mobile = models.IntegerField(default=None)
gender = models.CharField(max_length=30, default='Female')
age = models.PositiveIntegerField(default=None, validators=[ MinValueValidator(18), MaxValueValidator(100)])
bride_account_status = models.BooleanField(default=False)
def __str__(self):
return self.fname + ' '+self.lname
class RegisterGroom(models.Model):
fname = models.CharField(max_length=200)
lname = models.CharField(max_length=200)
mobile = models.IntegerField(default=None)
gender = models.CharField(max_length=30, default='Male')
age = models.PositiveIntegerField(default=None, validators=[ MinValueValidator(18), MaxValueValidator(100)])
groom_account_status = models.BooleanField(default=False)
def __str__(self):
return self.fname + ' '+self.lname
class Likes(models.Model):
bride_profile = models.ManyToManyField(RegisterBride, related_name='bride_profile')
groom_profile = models.ManyToManyField(RegisterGroom, related_name='groom_profile')
likes = models.BooleanField(default=False)
created_date = models.DateField(auto_now_add=True)
Views
class RegisterGroomProfileViewSet(viewsets.ModelViewSet):
queryset = RegisterGroom.objects.all()
serializer_class = RegisterGroomSerializer
class RegisterBrideProfileViewSet(viewsets.ModelViewSet):
queryset = RegisterBride.objects.all()
serializer_class = RegisterBrideSerializer
class LikesViewSet(viewsets.ModelViewSet):
queryset = Likes.objects.all()
serializer_class = LikesSerializer
Serializers
class RegisterGroomSerializer(serializers.ModelSerializer):
class Meta:
model = RegisterGroom
fields = '__all__'
class RegisterBrideSerializer(serializers.ModelSerializer):
class Meta:
model = RegisterBride
fields = '__all__'
class LikesSerializer(serializers.ModelSerializer):
groom_profile_s = RegisterGroomSerializer(many=True, required=True)
bride_profile_s = RegisterBrideSerializer(many=True, required=True)
class Meta:
model = Likes
fields = '__all__'
extra_kwargs = {'likes': {'required': True}}
def create(self, data):
if data['bride_profile'] == data['groom_profile']:
raise ValidationError(detail='To Profile ID == From Profile ID. They should be different.')
return data
Request
Request URL: http://127.0.0.1:8000/likes/
Request Method: POST
Status Code: 400 Bad Request
{'likes': True,
'bride_profile': 1,
'groom_profile': 6}
Response
POST /likes/
HTTP 400 Bad Request
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"groom_profile_s": [
"This field is required."
],
"bride_profile_s": [
"This field is required."
]
}
Server Error:
..\rest_framework\pagination.py:200: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'register.models.Likes'> QuerySet.
paginator = self.django_paginator_class(queryset, page_size)
Please suggest me.
Your request is not good : you put groom_profile_s and bride_profile_s as required but you do not send them in your request !
Request URL: http://127.0.0.1:8000/likes/
Request Method: POST
Status Code: 400 Bad Request
{'likes': True,
'groom_profile_s': [1],
'bride_profile_s': [6]}
After modifying your query, you should update your serializer.
class LikesSerializer(serializers.ModelSerializer):
groom_profile_s = RegisterGroomSerializer(many=True, required=True)
bride_profile_s = RegisterBrideSerializer(many=True, required=True)
class Meta:
model = Likes
fields = '__all__'
extra_kwargs = {'likes': {'required': True}}
def create(self, data):
if 'groom_profile_s' in data.keys():
groom_profile_s = validated_data.pop('groom_profile_s')
// Deal with your data here by creating the Like
// Same for bride_profile_s

validating json fields

This is my serializer file
from rest_framework import serializers
#All validations here
class BudgetSerializer(serializers.Serializer):
start_date = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
end_date = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
lifetime_budget = serializers.FloatField(max_value=None, min_value=None,allow_null=True)
def validate(self, attrs):
if attrs['start_date'] > attrs['end_date']:
raise serializers.ValidationError("start_date must be less than end date")
return attrs
class IoSerializer(serializers.Serializer):
name = serializers.CharField()
state = serializers.CharField()
currency = serializers.CharField(read_only=True)
budget_type = serializers.CharField(read_only=True)
budget_intervals = BudgetSerializer(many=True)
Json which i am serializing has 1000's of fields but i only want to validate few fields and return all json fields.But problem with serialzer is that it only outputs json consisting of mentioned field in serialiser.
Like i am sending json with 1000s of object names but it only return json which consist of start_date,end_date,lifetime_budget,name because it is mentioned in serializer file.I wanted to only validate few fields so i wrote them in serializer file but only this fields are returned.
My views.py code
class InsertionOrderViewset(viewsets.ViewSet):
def create(self, request, format=None):
advertiser_id = request.query_params.get('advertiser_id', None)
data = json.dumps(request.data)
io_object = json.loads(data, object_hook=lambda x: namedtuple('io_object', x.keys())(*x.values()))
serializer = IoSerializer(data=request.data, instance=io_object)
serializer.is_valid(raise_exception=True)
response_data = IO.create(data=request.data, params={"advertiser_id": advertiser_id })
return Response(request.data)
serializers.data method will only return values which are supplied to it unless you declare some read_only fields and or do any of these - supply an instance during serializer initialization, return an instance from serializer.save method. When serializer.instance has a value then read_only fields are returned with serializer.data. Your use case suggests that the incoming JSON object has 1000s of data of which only a small portion requires validation. My suggestion will be not to pass all the data, pluck only the necessary portion of the data, send them to serializer and do validation, return serializer.errors if any else rejoin the serializer.data with the incoming JSON and return the JSON. Since, majority of the data has nothing to do with validation, passing them in to the serializer will cause significant performance issue due to the way drf-serializer validates and maps values.
Still if you still want to supply all of the JSON to serializer and validate only a few consider supplying an instance when initializing serializer, dynamically declare ReadOnlyField in your serializer.__init__
serializers.py
from rest_framework import serializers
# default attributes of objects
default_attrs = ['__doc__', '__module__', '__slots__', '_fields', '_source']
class BudgetSerializer(serializers.Serializer):
start_date = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
end_date = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
lifetime_budget = serializers.FloatField(max_value=None, min_value=None,allow_null=True)
def __init__(self, *args, **kwargs):
super(BudgetSerializer, self).__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
for attr in dir(instance):
if attr not in self.fields and not callable(getattr(instance, attr)) and attr not in default_attrs:
self.fields[attr] = serializers.ReadOnlyField()
def validate(self, attrs):
if attrs['start_date'] > attrs['end_date']:
raise serializers.ValidationError("start_date must be less than end date")
return attrs
class IoSerializer(serializers.Serializer):
name = serializers.CharField()
state = serializers.CharField()
currency = serializers.CharField(read_only=True)
budget_type = serializers.CharField(read_only=True)
budget_intervals = BudgetSerializer(many=True)
def __init__(self, *args, **kwargs):
super(IoSerializer, self).__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
for attr in dir(instance):
if attr not in self.fields and not callable(getattr(instance, attr)) and attr not in default_attrs:
self.fields[attr] = serializers.ReadOnlyField()
inside views.py
import json
from collections import namedtuple
# inside view
data = json.dumps(request.data)
io_object = json.loads(data, object_hook=lamdba x: namedtuple('io_object', x.keys())(*x.values()))
serializer = IoSerializer(data=request.data, instance=io_object)
serializer.is_valid(raise_exception=True)
print(serializer.data)

django rest_framework serializer parameter

I am using django rest_framework to provide jsGrid json data.
As the rest_framwork example, I create a object fit the jsGrid format
class jsGridResp(object):
def __init__(self, data, itemsCount):
self.data = data
self.itemsCount = itemsCount
and the class based view, create a get function
class RateListViewSet(mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
"""
API endpoint that allows user to be viewed or edited
"""
queryset = RateList.objects.all().order_by('-create_date')
serializer_class = RateListSerializer
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
obj = queryset.get(pk=self.request.POST["id"])
self.check_object_permissions(self.request, obj)
return obj
def get(self, request, format=None):
pageIndex = request.GET.get('pageIndex')
pageSize = request.GET.get('pageSize')
sortField = request.GET.get('sortField', 'id')
sortOrder = request.GET.get('sortOrder', 'asc')
sortOrder = "" if sortOrder == "asc" else "-"
rows = RateList.objects.all().order_by("{}{}".format(sortOrder, sortField))
itemsCount = rows.count()
paginator = Paginator(rows, pageSize)
try:
rows = paginator.page(pageIndex)
except PageNotAnInteger:
rows = paginator.page(1)
except EmptyPage:
rows = paginator.page(paginator.num_pages)
result = jsGridResp(data=rows, itemsCount=itemsCount)
serializer = RateListGetSerializer(result)
json = JSONRenderer().render(serializer.data)
return Response(json)
then I create two serializer to serialize the data
class RateListSerializer(serializers.ModelSerializer):
class Meta:
model = RateList
fields = ('id', 'rate_code', 'hr01', 'hr02', 'hr03', 'hr04', 'hr05', 'hr06',
'hr07', 'hr08', 'hr09', 'hr10', 'hr11', 'hr12', 'hr13', 'hr14',
'hr15', 'note', 'create_date', 'update_date')
read_only_fields = ('create_date', 'update_date')
def update(self, instance, validated_data):
result = instance.update(id=instance.id, **validated_data)
return result
def destroy(self, instace, validated_data):
return "{seccuess: true}"
class RateListGetSerializer(serializers.Serializer):
itemsCount = serializers.IntegerField()
data = RateListSerializer(many=True)
but I have many model need to do like these.
can I use just one serializer to serialize all model.
I want to create a jsGridGetSerializer can pass in a model parameter, so I don't need to create many simple serialzer to do the same thing.
Is this possible?
If I understand your problem correctly, you want a generic serializer which accepts a Model variable as a parameter in its Meta class.
One way to do this is to pass in the model name as a url keyword argument, then catching it in views.py. Then you can override the Meta model via overriding the get_serializer_class:
serializers.py
class GenericSerializer(serializers.ModelSerializer):
class Meta:
model = None
views.py
class GenericViewSet(viewsets.ModelViewSet):
def get_queryset(self):
model = self.kwargs.get('model')
return model.objects.all()
def get_serializer_class(self):
GenericSerializer.Meta.model = self.kwargs.get('model')
return GenericSerializer

Resources