I am trying to filter a queryset based on input date value from a form but I am not certain on how to access the "date" value.
Forms.py;
class DateInput(forms.DateInput):
input_type = 'date'
Models.py :
class Milk(models.Model):
milking_date = models.DateTimeField(auto_now_add=True)
Queryset generating function trial in admin.py class, nothing works.
...
def milk_record_per_day(self, request):
if request.method == "POST":
form = forms.DateInputForm(request.POST)
slct_date = form.date
data = (models.Milk.objects.filter(milking_date__date=slct_date)
.annotate(date=functions.TruncDate("milking_date"))
.values("date")
...
I expect to return a list of milk objects based on date input from the form, So far I get this error on POST request
'DateInput' object has no attribute 'date'
You are making things too complicated. You can create a form with this:
class DateInput(forms.DateInput):
input_type = 'date'
class DateInputForm(forms.Form):
date = forms.DateField(widget=DateInput)
and then process this in the view:
def milk_record_per_day(self, request):
if request.method == "POST":
form = forms.DateInputForm(request.POST)
if form.is_valid():
data = models.Milk.objects.filter(milking_date__date=form.cleaned_data['date'])
# ...
I have the following code working perfectly. I can create a Post object from DRF panel by selecting an image and a user. However I want DRF to populate the user field by the currently logged in user.
models.py
class Post(TimeStamped):
user = models.ForeignKey(User)
photo = models.ImageField(upload_to='upload/')
hidden = models.BooleanField(default=False)
upvotes = models.PositiveIntegerField(default=0)
downvotes = models.PositiveIntegerField(default=0)
comments = models.PositiveIntegerField(default=0)
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'user', 'photo']
views.py
class PhotoListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.filter(hidden=False)
serializer_class = PostSerializer
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
How can I do this?
Off the top of my head, you can just override the perform_create() method:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Give that a shot and let me know if it works
You can use CurrentUserDefault:
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
It depends on your use case. If you want it to be "write-only", meaning DRF automatically populates the field on write and doesn't return the User on read, the most straight-forward implementation according to the docs would be with a HiddenField:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault(),
)
If you want want it to be readable, you could use a PrimaryKeyRelatedField while being careful that your serializer pre-populates the field on write - otherwise a user could set the user field pointing to some other random User.
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.PrimaryKeyRelatedField(
# set it to read_only as we're handling the writing part ourselves
read_only=True,
)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Finally, note that if you're using the more verbose APIView instead of generics.ListCreateAPIView, you have to overwrite create instead of perform_create like so:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.PrimaryKeyRelatedField(
read_only=True,
)
def create(self, validated_data):
# add the current User to the validated_data dict and call
# the super method which basically only creates a model
# instance with that data
validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).create(validated_data)
You can avoid passing the user in your request and you won't see it in the output but DRF will populate it automatically:
from rest_framework import serializers
class MyModelSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = models.MyModel
fields = (
'user',
'other',
'fields',
)
As of DRF version 3.8.0 (Pull Request discussion), you can override save() in serializer.
from rest_framework import serializers
...
class PostSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Post
fields = ['id', 'user', 'photo']
def save(self, **kwargs):
"""Include default for read_only `user` field"""
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)
#DaveBensonPhillips's answer might work in your particular case for some time, but it is not very generic since it breaks OOP inheritance chain.
ListCreateAPIView inherits from CreateModelMixin which saves the serializer already. You should always strive to get the full chain of overridden methods executed unless you have a very good reason not to. This way your code stays DRY and robust against changes:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).perform_create(serializer)
You will have to override the default behavior of how generics.ListCreateAPIView creates an object.
class PhotoListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.filter(hidden=False)
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
def get_serializer_class(self):
if self.request.method == 'POST':
return CreatePostSerializer
else:
return ListPostSerializer
def create(self, request, *args, **kwargs):
# Copy parsed content from HTTP request
data = request.data.copy()
# Add id of currently logged user
data['user'] = request.user.id
# Default behavior but pass our modified data instead
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
The .get_serializer_class() is not necessary as you can specify which fields are read-only from your serializer, but based on the projects I have worked on, I usually end up with 'asymmetric' serializers, i.e. different serializers depending on the intended operation.
Try this:
def post(self, request, format=None)
serializer = ProjectSerializer(data=request.data)
request.data['user'] = request.user.id
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST
This is what works for me in serializers.py, where I am also using nested data. I want to display created_by_username without having to lookup other users.
class ListSerializer(serializers.ModelSerializer):
"""
A list may be created with items
"""
items = ItemSerializer(many=True)
# automatically set created_by_id as the current user's id
created_by_id = serializers.PrimaryKeyRelatedField(
read_only=True,
)
created_by_username = serializers.PrimaryKeyRelatedField(
read_only=True
)
class Meta:
model = List
fields = ('id', 'name', 'description', 'is_public',
'slug', 'created_by_id', 'created_by_username', 'created_at',
'modified_by', 'modified_at', 'items')
def create(self, validated_data):
items_data = validated_data.pop('items', None)
validated_data['created_by_id'] = self.context['request'].user
validated_data['created_by_username'] = self.context['request'].user.username
newlist = List.objects.create(**validated_data)
for item_data in items_data:
Item.objects.create(list=newlist, **item_data)
return newlist
I wrote an extension to DRF's serializer below
from rest_framework import serializers
class AuditorBaseSerializer(serializers.Serializer):
created_by = serializers.StringRelatedField(default=serializers.CurrentUserDefault(), read_only=True)
updated_by = serializers.StringRelatedField(default=serializers.CurrentUserDefault(), read_only=True)
def save(self, **kwargs):
# if creating record.
if self.instance is None:
kwargs["created_by"] = self.fields["created_by"].get_default()
kwargs["updated_by"] = self.fields["updated_by"].get_default()
return super().save(**kwargs)
and it can be used as follows
class YourSerializer(serializers.ModelSerializer, AuditorBaseSerializer):
class Meta:
model = SelfEmployedBusiness
fields = (
'created_by',
'updated_by',
)
I am trying to pass a django model object to a field in a serializer that is for a foreign key field in the model. However, I get the error: "Object of type AuthorUser is not JSON serializable."
Here is the model the serializer is for:
class Article(models.Model):
title = models.CharField(max_length=200, unique=True)
author = models.ForeignKey(AuthorUser, on_delete=models.CASCADE)
body = models.TextField()
posted=models.BooleanField(default=False)
edited = models.BooleanField(default=False)
ready_for_edit = models.BooleanField(default=False)
Here is the serializer (author is the field specifically that is giving me trouble):
class CreateArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['title', 'body', 'author']
And here is the view that has the code that causes the error (the POST method is the part that causes the error):
#api_view(['GET', 'POST'])
def articles(request):
if request.method == 'GET':
articles = Article.objects.all()
serializer = CreateArticleSerializer(articles, many=True)
return Response(serializer.data)
if request.method == 'POST':
if request.user.is_authenticated:
author = AuthorUser.objects.get(id=request.data['author'])
request.data['author'] = author
print(request.data)
serializer = CreateArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return HttpResponse(status=401)
Any help is appreciated! Just to let you know, when creating these articles using an id, it works, however, it creates a new field in Article called author_id. Then when I try to access author it gives me author_id so that doesn't work.
You need to change your serializer as follows:
class CreateArticleSerializer(serializers.ModelSerializer):
author = serializers.SlugRelatedField(queryset=AuthorUser.objects.all(),
slug_field='id')
class Meta:
model = Article
fields = ['title', 'body', 'author']
Now if you will pass id in your view while calling serializer it will create the object of model. Hope this will work for you.
I am working on a Django application with two Models.
Messages - that contain twitter style messages
Feedback - response to messages including comments, likes, dislikes
I am writing APIView for Feedback, but it is not GETing or POSTing the relevant messages even though I can browse through the admin panel.
The code is as follows:
Models:
class Messages(models.Model):
postIdentifier = models.AutoField(primary_key=True)
title = models.CharField(max_length=100)
message = models.TextField(null=False)
class Feedback(models.Model):
isLiked = models.BooleanField(null=True)
isDisliked = models.BooleanField(null=True)
comment = models.TextField(null=True)
post = models.ForeignKey(Messages, on_delete=models.CASCADE)
Serializers:
class MessagesSerializer(serializers.ModelSerializer):
class Meta:
model = Messages
fields = ['postIdentifier', 'title', 'message']
class FeedbackSerializer(serializers.ModelSerializer):
message = MessagesSerializer()
class Meta:
model = Feedback
fields = ['isLiked', 'isDisliked', 'comment', 'post']
APIView for Feedback model
def get(self, request, id):
related_message_object = Messages.objects.filter(id)
feedback = Feedback.objects.filter('post'= related_message_object)
serializer = FeedbackSerializer(feedback)
return Response({
'data': serializer.data
})
def post(self, request, *args, **kwargs):
serializer = FeedbackSerializer(data= request.data)
if serializer.is_valid():
serializer.save()
return Response({
'message': 'Feedback data posted successfully!',
'data': serializer.data
})
The URLPattern for the feedback is
path('feedback/< id >/', FeedbackAPIView.as_view()),
#I am aware of spaces here on either side of id as I am unable to post the id with brackets on.
I am going through the rest_framework documentation, but I can not find relevant documentation with regard to the problem. Can anyone kindly point out my mistake in the get() method of FeedbackAPIView. I am hoping to achieve that when I browse feedback/2/ I want to see feedback relavant to Message with postIdentifier = 2. Any feedback is much appreciated!
As the feedback resource url is path('feedback/< id >/', FeedbackAPIView.as_view()),
then at def get(self, request, id): you should filter feedback by id :
feedback = Feedback.objects.filter(id=id).first()
I'm new with Django and I'm having some problems creating a custom user model. I followed every steps from the django documentation. Here is my model :
class UserProfile(models.Model):
user = models.OneToOneField(User)
comment = models.BooleanField()
score = models.IntegerField(null=True)
profilpic = models.ImageField(upload_to="/profilepics")
bio = models.CharField(max_length=140)
Then I created several users with django-registration. But when I go to the admin and I try to delete a user I created or when I just try to click on the username, I get this error:
AttributeError at /admin/auth/user/3/
'UserProfile' object has no attribute 'username'
Exception Value:
'UserProfile' object has no attribute 'username'
Exception Location: /Users/marc-antoinelacroix/Desktop/Site/sportdub/projet/models.py in __unicode__, line 14
So I think I have to create a "username" in my UserProfile model, and associate it to the username of the django's User, but I have no idea how to do it...
Any help would be welcome.
Thanks!
It seems like you're trying to access
def __unicode__(self):
return self.username
but it has to be
def __unicode__(self):
return self.user
Here's a demo
project/account/models.py
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
homepage = models.URLField(verify_exists=False)
#...
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
project/account/admin.py
from django.contrib import admin
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from account.models import UserProfile
admin.site.unregister(User)
class UserProfileInline(admin.StackedInline):
model = UserProfile
class UserProfileAdmin(UserAdmin):
inlines = [UserProfileInline]
admin.site.register(User, UserProfileAdmin)
project/settings.py
AUTH_PROFILE_MODULE = "account.userprofile"
No, you need to define UserProfile.__unicode__() properly. It needs to get the username from the related User model.