DRF Read only field is still validated - python

I have a field that I want always to be the user. My serializer is like this:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
read_only_fields = ('user',)
def perform_save(self, serializer):
serializer.save(user=self.request.user)
class MyModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
...
But it gives me the error NOT NULL constraint failed: app_my_model.user_id but the field is read_only... I don't get this.

First of all, there is no method named perform_save() for a serializer, it's for the viewset class. This may be the problem
Use the save() method as below
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
read_only_fields = ('user',)
def save(self, **kwargs):
kwargs['user'] = self.context['request'].user
return super().save(**kwargs)

Related

how to customize fields in nested serializer?

class ListSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = JobseekerProfile
fields = ('user',)
*How to modify this fields so that I can add only one field from user like user.username? *
You can add ReadOnlyField Field in the serializer. This field only use when you try to retrieve your data. (GET method)
class ListSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
class Meta:
model = JobseekerProfile
fields = ('user',)
class ListSerializer(serializers.ModelSerializer):
user = serializers.CharField(read_only=True, source='user.username')
class Meta:
model = JobseekerProfile
fields = ('user',)
Try it

How to post with references in Django Rest Framework

I have Comment model related with User model
# models.py
class Comment(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
more fields...
....
In the serializer I want to do a create (POST) of a user comment.
But the post method is not enabled, only the put or patch method
Example: User Jon wants to create a comment
# serializers.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
class UserCommentSerializer(serializers.ModelSerializer):
# id of comment
url = serializers.HyperlinkedIdentityField(
view_name="user-comments-detail",
read_only=True
)
id = serializers.CharField(read_only=True)
comment = CommentSerializer()
class Meta:
model = User
fields = ['id', 'comment']
def create(self, validated_data):
comment_data = validated_data.pop('comment')
user = User.objects.create(**validated_data)
Comment.objects.create(user=user, **comment_data)
return user
I want to new comment, referencing the user
# views.py
class CommentViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserCommentSerializer
But I have an exception, that the user has no comment RelatedObjectDoesNotExist
My url like this
http://localhost:8000/user-comments/10
10 is a user_id pk
{} object post
Example: Comment.objects.create(user=pk, {})
Currently, only put and patch is enabled, but what I want to do is post of user
{
"url": "http://localhost:8000/user-comments/10",
"id": "10",
"comment": null
}
Comment does not exist
Any idea or suggestion?
You actually need just one serializer for that.
This will create a comment for the current logged in user.
# serializers.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
read_only_fields = ['user']
def create(self, validated_data):
# get the user who sent the request
user = self.context['request'].user
return Comment.objects.create(user=user, **validated_data)
# views.py
class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
def get_serializer_context(self):
# this is important since you want to pass the request object to your serializer
context = super().get_serializer_context()
context.update({"request": self.request})
return context
The exception you are getting is because ModelSerializer is linked to a specific model, in this case you linked UserCommentSerializer to model User. Variable Meta.fields specifies which fields of the model are being returned by the serializer and, so, you are getting an exception because such variable is set to ('id', 'comment') and model User doesn't have a field comment.
You can achieve what you want, this way:
class UserCommentSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id']
extra_kwargs = {'comment': {'write_only': True}}
def create(self, validated_data):
comment_data = validated_data.pop('comment')
user = User.objects.create(**validated_data)
Comment.objects.create(user=user, comment=comment_data)

How to update with SerializerMethodField in Django Rest Framework

I have a field in my ModelSerializer which I've set as SerializerMethodField to modify the get behaviour for the field. I could update the data before, now I can't. How can I solve this?
Initially, without using SerializerMethodField, I got data like this:
{
...
"members": [2,3],
...
}
but I added SerializerMethodField to modify the data, then update stopped working.
models.py
# Create your models here.
class Company(models.Model):
members = ArrayField(models.IntegerField(blank=True), blank=True)
...
serializers.py
class AccountSerializer(serializers.ModelSerializer):
user=serializers.StringRelatedField(read_only=False)
class Meta:
model=Account
fields='__all__'
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
members = serializers.SerializerMethodField()
class Meta:
model = Company
fields = '__all__' #('id', 'name', 'description', 'date_created', 'user', 'status', 'theme', 'members')
def get_members(self, obj):
accounts = Account.objects.filter(id__in=obj.members)
return AccountSerializer(accounts, many=True).data
...
You need to use different serializers for update and create. This serializer works for get only.
Or, you can create a custom field. Django Rest Framework How to update SerializerMethodField
Or, there can be other simpler hooks. If 'create' and 'update' worked as you wanted before modifiying members, then you can do as follow to get everything to default for create and update requests.
Instead of using SerializerMethodField, override serializer representation.
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
class Meta:
model = Company
fields = ('id', 'name', 'description', 'date_created', 'user', 'status', 'theme', 'members', 'members_data')
def to_representation(self, obj)
ret = super().to_representation(obj)
ret["members"] = AccountSerializer(accounts, many=True).data
return ret
Override the __init__ method .
.
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
try:
if self.context['request'].method in ['GET']:
self.fields['members'] = SerializerMethodField()
except KeyError:
pass
class Meta:
model = Company
fields = '__all__' #('id', 'name', 'description', 'date_created', 'user', 'status', 'theme', 'members')
def get_members(self, obj):
accounts = Account.objects.filter(id__in=obj.members)
return AccountSerializer(accounts, many=True).data
...
Or, you can create different field for getting members.
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
members_data = SerializerMethodField()
class Meta:
model = Company
fields = ('id', 'name', 'description', 'date_created', 'user', 'status', 'theme', 'members', 'members_data')
def get_members_data(self, obj):
accounts = Account.objects.filter(id__in=obj.members)
return AccountSerializer(accounts, many=True).data
...

Django CreateView - Display only particular objects in foreignkey field

I have a CreateView view that holds a bunch of fields that need to be filled by the user when creating a new contact. Now, I want the user to be able to see and choose only from the categories that they'd created.
This is the model of Category:
class Category(models.Model):
class Meta:
verbose_name = _('category')
verbose_name_plural = _('categories')
name = models.CharField(max_length=100, unique=True)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
def __unicode__(self):
return self.name
This is the view:
class ContactCreate(LoginRequiredMixin, generic.edit.CreateView):
model = models.Contact
success_url = reverse_lazy('site:contacts')
fields = ['firstname', 'lastname', 'phone1', 'phone2', 'email', 'city', 'category']
template_name = 'site/contacts.html'
context_object_name = 'all_contacts'
What I need the user to see is a select that has only the categories which include the appropriate profile foreign key associated with them.
I'd be glad to get some help with this. Thank you!
You can override the get_form method of the view and set the queryset of the appropriate field:
class ContactCreate(LoginRequiredMixin, generic.edit.CreateView):
# ...
def get_form(self, *args, **kwargs):
form = super(ContactCreate, self).get_form(*args, **kwargs)
form.fields['categories'].queryset = Category.objects.filter(profile=self.request.user.profile)
return form
This, of course, assumes that your Profile model has a OneToOneField to User with related_name 'profile', otherwise you'd have to adjust the filtering.

Pass extra arguments to nested Serializer in Django Rest Framework

I have such serializer:
class FirstModelSerializer(serializers.ModelSerializer):
secondModel = SecondModelSerializer()
class Meta:
model = FirstModel
fields = '__all__'
Where secondModel is ManyToMany field of FirstModel.
Is there any way to pass FirstModel object id to SecondModelSerializer?
It was easier then I thought. I just had to use context like this
class FirstModelSerializer(serializers.ModelSerializer):
secondModel = SerializerMethodField()
class Meta:
model = FirstModel
fields = '__all__'
def get_secondModel(self, obj):
return SecondModelSerializer(obj.secondModel.all(), many=True, context={'first_model_id': obj.id)).data
And use self.context.get('first_model_id') in SecondModelSerializer to get to this id.

Categories