'TaskSerializer' object has no attribute 'start_date' - django-rest-framework

Hope You Are Good!
My question is how to get user input data in perform create method?
like in Django I use:
form.some_field
to get the field data but i can't do this in rest framework:
def perform_create(self, serializer):
if serializer.start_date > date.today():
serializer.status = "Schedule"
serializer.save(user=self.request.user)
I get this issue:
'TaskSerializer' object has no attribute 'start_date'
here is my model:
class Task(models.Model):
status_options = (("In Progress","In Progress",),("Cancelled","Cancelled",),("Completed","Completed",),("Schedule","Schedule",),)
priority_options = (("Low","Low",),("Medium","Medium",),("High","High",),)
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
description = models.TextField()
priority = models.CharField(max_length=124, choices=priority_options, default="Low")
status = models.CharField(max_length=124, choices=status_options, default="In Progress")
start_date = models.DateField()
end_date = models.DateField()
timestamp = models.DateField(auto_now_add=True)
altimedatetamp = models.DateTimeField(auto_now_add=True)
recurring = models.BigIntegerField(default=0)
....

You have to access serializer.validated_data to get your model field value.
And instead of manipulating the status value inside the serializer, consider passing it along the save method like you do with the user.
Your perform_create should look something like that:
def perform_create(self, serializer):
if serializer.validated_data['start_date'] > date.today():
serializer.save(user=self.request.user, status='Schedule')
else:
serializer.save(user=self.request.user)

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

TypeError: Field 'id' expected a number but got <class 'rest_framework.fields.CurrentUserDefault'>

Seems I need to apply a dot notation to CurrentUserDefault() object, tried .id but failed
class DotPrivateSerializer(serializers.ModelSerializer):
tag = serializers.SerializerMethodField()
def get_tag(self,obj):
queryset=TagPrivate.objects.filter(user=serializers.CurrentUserDefault) # <--TypeError
return TagPrivateSerializer(queryset).data
models.py
class DotPrivate(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
name = models.CharField(max_length=255)
description = models.TextField(max_length=350, blank=True)
lon = models.CharField(max_length=20)
lat = models.CharField(max_length=20)
rating = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(5.0)])
tag = models.ForeignKey('TagPrivate', on_delete=models.PROTECT)
in the following link in the first answer I found some solution but I do not completly understand it:
'CurrentUserDefault' object has no attribute 'user'
class TagPrivateSerializer(serializers.ModelSerializer):
class Meta:
model = TagPrivate
fields = ('id', 'name')
read_only_fields = ('id',)
You can not use CurrentUserDefault, this is just a value that the Django serializer will use for a default=… parameter, and then later swap for the request user.
You can fetch this from the request in the context, so:
class DotPrivateSerializer(serializers.ModelSerializer):
tag = serializers.SerializerMethodField()
def get_tag(self, obj):
queryset = TagPrivate.objects.filter(user=self.context['request'].user)
return TagPrivateSerializer(queryset).data
In the ModelViewSet, you will need to pass the user, so:
class DotPrivateViewSet(ModelViewSet):
queryset = # …
permission_classes = # …
serializer_class = DotPrivateSerializer
def get_serializer_context(self):
context = super().get_serializer_context()
context.update(request=self.request)
return context

Change model field if requested particular url

Im making API using django rest framework . I only want to change one field in model which is the read field if i go to a particular url
my model:
class Notification(PolymorphicModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_by = models.ForeignKey(ElsUser, on_delete=models.CASCADE, default=None, related_name="creatednotifications")
created_on = models.DateTimeField(default=timezone.now)
created_for = models.ForeignKey(ElsUser, on_delete=models.CASCADE, default=None, related_name="receivednotifications")
read = models.DateTimeField(default=None, null=True, blank=True)
message = models.CharField(default=None, blank=True, null=True, max_length=800)
The APis i made lists the notifications for a logged in user.
What i want to implement is that :
notification/<:id>/markread
notification/<:id>/markunread
If i go to this particular url i want to modify the read field ..For example make it None if to mark unread. Also i need to check if the logged in user has received the notification with that id.I know the basics and how to create the urls
class NotificationMarkRead(generics.UpdateAPIView):
serializer_class = NotificationSerializer
def get_queryset(self):
queryset = Notification.objects.filter(created_for=self.request.user)
return queryset
class NotificationMarkUnread(generics.UpdateAPIView):
serializer_class = NotificationSerializer
def get_queryset(self):
queryset = Notification.objects.filter(created_for=self.request.user)
return queryset
def update
My initial try is to override the put method in update_API view
Write a simple function:
#api_view(['PUT'])
def notification_toggle_read_status(request, pk, read_status):
notification = Notification.objects.get(pk=pk)
if read_status == 'markread':
notification.read = timezone.now()
else:
notification.read = None
notification.save(update_fields=['read'])
serializer = NotificationSerializer(instance=notification)
return Response(serializer.data)
use this url path:
notifications/<int:pk>/<string:read_status>/
As you have already coding with DRF why not try with viewset link . And from front-end just pass update fields with put request.

DRF - html post options only for user logged in

I have the following serializer:
class WidgetSerializer(serializers.ModelSerializer):
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model=Widget
fields = ('id', 'title', 'description', 'username', 'code', 'owner', 'list')
The problem is that the 'list' field, which is a drop down, gives all lists whereas I only want it to display lists that are owned by the user currently logged in.
Here's the respective models:
class WidgetList(MPTTModel):
name = models.CharField(max_length=100)
description = models.CharField(max_length=1024)
owner = models.ForeignKey('MyUser')
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
def __str__(self):
return self.name
class MPTTMEta:
order_insertion_by = ['name']
class Widget(models.Model):
title = models.CharField(max_length=100)
description = models.CharField(max_length=1024)
username = models.CharField(max_length=50)
code = models.CharField(max_length=1024)
owner = models.ForeignKey('MyUser', related_name='MyUser_owner')
list = models.ForeignKey('WidgetList')
I am a beginner in django. I hope that I could help.
Just try this
WidgetList.objects.filter(owner=request.user)
I have to limit it through a SlugRelatedField as per the documentation here -
http://www.django-rest-framework.org/api-guide/relations/#slugrelatedfield
I then used it like so -
list = serializers.SlugRelatedField(
queryset=WidgetList.objects.filter(owner=3),
many=True,
slug_field='name'
)
All I need to figure out now is to pass the serializers.CurrentUserDefault() in the filter for the queryset, or pass request.user.

Passing argument from view to Custom RelatedField serializer

How can I pass an argument to a serializers.RelatedField class from views.py. I need to pass language_id to query Language.objects model within that RelatedField.
I am not sure if I took a right approach to this issue. What I want to achieve is to present information about genres associated to a movie from database model about depending on the language. The MovieGenre model has genre ID field which I want to replace with actual Genre name.
My serialiser.py
class GenreField(serializers.RelatedField):
def to_representation(self, value, language_id=1):
genre_name = GenresVideo.objects.get(genre_id=value, language_id=language_id)
return genre_name.name
class MovieGenresSerializer(serializers.ModelSerializer):
genre_id = GenreField(read_only=True)
class Meta:
model = MoviesGenres
As you see, here I query Language.objects with default value but I would like to pass it from views (language_id).
My views.py:
class MovieGenresTestViewSet(viewsets.ModelViewSet):
lookup_field = 'movie'
queryset = MoviesGenres.objects.all()
serializer_class = MovieGenresSerializer
def list(self, request, language_pk):
queryset = MoviesGenres.objects.all()
serializer = MovieGenresSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, movie, language_pk):
queryset = MoviesGenres.objects.filter(movie=movie)
serializer = MovieGenresSerializer(queryset, many=True)
return Response(serializer.data)
And my urls.py:
router.register(r'lang', LanguagesViewSet, base_name='lang')
mov_gen = routers.NestedSimpleRouter(router, r'lang', lookup='language')
mov_gen.register(r'mg', MovieGenresTestViewSet, base_name='mg')
url(r'^api/', include(genre_spec.urls))
My models.py
class Languages(models.Model):
name = models.CharField(unique=True, max_length=255)
short_name = models.CharField(unique=True, max_length=4, blank=True, null=True)
active = models.BooleanField(default="")
class Meta:
managed = False
db_table = 'languages'
ordering = ('id',)
class GenresVideo(models.Model):
genre_id = models.IntegerField()
language = models.ForeignKey('Languages')
name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'genres_video'
unique_together = (('genre_id', 'language'),)
ordering = ('genre_id',)
class MoviesGenres(models.Model):
movie = models.ForeignKey(Movies)
genre_id = models.IntegerField()
class Meta:
managed = False
db_table = 'movies_genres'
unique_together = (('movie', 'genre_id'),)
Through the urls routes, I can get a correct response from API including the language_id. I just need to pass it to the view somehow.
Thanks a lot for help!
I'll try to answer to your first question, with the easiest implementation possible: SerializerMethodField. Because we will get the language id via the context passed to the serializer, we should either generate the context for the serializer, or let the framework do that for us.
Now to the problem at hand: you aren't filtering the queryset (MoviesGenres) by language per se. Thus, we can avoid overwriting the list and retrieve methods. Nevertheless, the router mechanism will inject in kwargs for the view method the language_pk parameter - that's the parameter that we will retrieve from within the serializer context:
class MovieGenresSerializer(serializers.ModelSerializer):
genre = searializers.SerializerMethodField()
class Meta:
model = MoviesGenres
def get_genre(self, instance):
# get the language id from the view kwargs
language_id = self.context['view'].kwargs['language_pk']
# get the genre
try:
genre_name = GenresVideo.objects.get(genre_id=instance.genre_id, language_id=language_id).name
except GenresVideo.DoesNotExist:
genre_name = None
# return the formatted output
return genre_name
class MovieGenresTestViewSet(viewsets.ModelViewSet):
lookup_field = 'movie'
queryset = MoviesGenres.objects.all()
serializer_class = MovieGenresSerializer

Resources