from the documentation:
read_only
Set this to True to ensure that the field is used when serializing a representation, but is not used when updating an instance during deserialization.
Defaults to False
required
Normally an error will be raised if a field is not supplied during deserialization. Set to false if this field is not required to be present during deserialization.
Defaults to True.
So I have a model which has a field that's not nullable but I want it to be populated in the pre_save method, so I have set the field to required=False in serializer, but doesn't seem to work. I am still getting error when saving the record.
class FavoriteListSerializer(serializers.ModelSerializer):
owner = serializers.IntegerField(required=False)
class Meta:
model = models.FavoriteList
Update:
I have added serializer_class = serializers.FavoriteListSerializer to the ViewSet, now instead of getting This field is required, which I think got past the validation but then I am getting This field cannot be null. I have checked the pre_save method is not being executed, any ideas?
Yeah, I ran into this issue at some point as well. You need to also update the validation exclusions.
class FavoriteListSerializer(serializers.ModelSerializer):
owner = serializers.IntegerField(required=False)
class Meta:
model = models.FavoriteList
def get_validation_exclusions(self):
exclusions = super(FavoriteListSerializer, self).get_validation_exclusions()
return exclusions + ['owner']
Late Entry to this thread. This issue was fixed in django-rest-framework 2.3.13. Here is the link of the PR.
You use it like this in your case:
class Meta:
model = models.FavoriteList
optional_fields = ['owner', ]
In case somebody lands here with a similar issue, pay attention to the following attributes along with required:
allow_blank:
If set to True then the empty string should be considered a valid value.
allow_null:
Normally an error will be raised if None is passed to a serializer field.
required:
Normally an error will be raised if a field is not supplied during deserialization.
I was straggling to figure out why I was getting a validation error with required=False where I had missed the allow_null attribute.
In 2020, for DRF 3.12.x, the approach that I prefer the approach that relies on
Serializer's extra_kwargs.
So assuming your
class FavoriteListSerializer(serializers.ModelSerializer):
owner = serializers.IntegerField(required=False)
class Meta:
model = models.FavoriteList
fields = ["owner"] # and whatever other fields you want to expose
extra_kwargs = {"owner": {"required": False, "allow_null": True}}
If you have unique_together constraint on one of the fields you are trying to set required=False you need to set validators=[] in serializers Meta like
class FavoriteListSerializer(serializers.ModelSerializer):
owner = serializers.IntegerField(required=False)
class Meta:
model = models.FavoriteList
validators = []
Here is the original answer
You can also do this:
class ASerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
...
As referred here: https://www.django-rest-framework.org/api-guide/validators/#advanced-field-defaults
There you can also find the case when you also wanna let the view show owner
I would set model field to allow null value (and possible also default to None)
class FavoriteList(models.Model):
owner = models.PositiveIntegerField(null=True, default=None)
Then it's possible to just leave owner field to Meta section. These fields, without any extra settings, will automatically get all attributes from model field and be non-required.
class FavoriteListSerializer(serializers.ModelSerializer):
class Meta:
model = models.FavoriteList
fields = ('owner',)
Related
I have a question regarding django rest framework.
Most of the time, I have a serializer which has some read-only fields. For example, consider this simple model below:
class PersonalMessage(models.Model):
sender = models.ForeignKey(User, related_name="sent_messages", ...)
recipient = models.ForeignKey(User, related_name="recieved_messages", ...)
text = models.CharField(...)
def __str__(self) -> str:
return f"{self.text} (sender={self.sender})"
In this model, the value of sender and recipient should be automatically provided by the application itself and the user shouldn't be able to edit those fields. Alright, now take a look at this serializer:
class PersonalMessageSerializer(serializers.ModelSerializer):
class Meta:
model = PersonalMessage
fields = '__all__'
read_only_fields = ('sender', 'recipient')
It perfectly prevents users from setting an arbitrary value on the sender and recipient fields. But the problem is, when these fields are marked as read-only in the serializer, the serializer will completely ignore all the values that are passed into the constructor for these fields. So when I try to create a model, no values would be set for these fields:
PersonalMessageSerializer(data={**request.data, 'sender': ..., 'recipient': ...) # Won't work
What's the best way to prevent users from setting an arbitrary value and at the same time auto-populate those restricted fields in django rest framework?
Depending on how you get those two objects, you can use the serializer's save method to pass them, and they will automatically be applied to the object you are saving:
sender = User.objects.first()
recipient = User.objects.last()
serializer = PersonalMessageSerializer(data=request.data)
message = serializer.save(sender=sender, recipient=recipient)
The kwargs should match the field names in your model for this to work. For reference, have a look here
You able to override the serializer context like this;
PersonalMessageSerializer(data={**request.data, context={'sender': sender, 'recipent': recipent})
and catch the context inside serializer.
class PersonalMessageSerializer(serializers.ModelSerializer):
class Meta:
model = PersonalMessage
fields = '__all__'
read_only_fields = ('sender', 'recipient')
def validate(self, attrs):
attrs = super().validate(attrs)
attrs['sender'] = self.context['sender']
attrs['recipent'] = self.context['recipent']
return attrs
now serializer.validated_data it must returns sender and recipent.
From the question it is not possible to understand what field(s) of the relationship with sender and recipient you want to interact with, but a general answer can be found in the Serializer relations section of Django REST documentation.
Long story short, if you want to interact with one field only, you can use SlugRelatedField, which lets you interact with the target of the relationship using only one of its fields.
If it just the id, you can use PrimaryKeyRelatedField.
If you want to interact with more than one field, the way to go is Nested Relationships. Here you can specify a custom serializer for the target relationship, but you will have to override the create() method in your PersonalMessageSerializer to create the object from your relationship, as nested serializers are read-only by default.
So this is how you can make set a default on create but read only after in DRF. Although in this solution it wont actually be readonly, it's writable, but you now have explicit control on what the logged in user can write, which is the ultimate goal
Given the model
class PersonalMessage(models.Model):
sender = models.ForeignKey(User,...)
recipient = models.ForeignKey(User,..)
text = models.CharField(...)
You would first create your own custom default (I will show an example for only one field)
# Note DRF already has a CurrentUserDefault you can also use
class CurrentSenderDefault:
requires_context = True
def __call__(self, serializer_field):
return serializer_field.context['request'].user
def __repr__(self):
return '%s()' % self.__class__.__name__
Next you make your own field, that knows whats up with the filter.
This queryset prevents people from setting a value they are not allowed to. which is exactly what you want
class SenderField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
user = self.context['request'].user
if user:
queryset = User.objects.filter(id=user.id)
else:
queryset = User.objects.none()
return queryset
Finally on the serialiser you go
class PersonalMessageSerializer(serializers.ModelSerializer):
sender = SenderField(default=CurrentSenderDefault())
recipient = ...
class Meta:
model = PersonalMessage
fields = '__all__'
read_only_fields = ('sender', 'recipient')
So I created a simple model as follows
class Titles(models.Model):
titleID = models.CharField(max_length=20,primary_key=True)
title = models.CharField(max_length=100)
class Meta:
verbose_name = "Titles"
verbose_name_plural = verbose_name
def __str__(self):
return self.title
Exposed it as a API as
class TitlesResource(AT.MultipartResource,AT.WrapView,ModelResource):
class Meta:
queryset = coreModels.Titles.objects.all()
authentication = AT.cxenseAMSAPIAuthentication()
authorization=Authorization()
resource_name = 'titles'
allowed_methods = ['get','post','put','patch']
include_resource_uri=False
limit=1000
When I try to create a new object it works but if I mess up any of the fields it still works
eg:
http://localhost:8000/core/titles/
{
"I_am_not_suppling_a_correct_feild": "2",
"title_not": "dept 1"
}
[27/Oct/2017 10:54:12] DEBUG [django.db.backends:90] (0.001) UPDATE "core_titles" SET "title" = '' WHERE "core_titles"."titleID" = ''; args=('', '')
Shouldnt this fail as I am not supplying the needed fields?
When I try to create a new object it works but if I mess up any of the fields it still works
post data can have N no. of fields. It depends how you are handling each one of them.
Shouldn't this fail as I am not supplying the needed fields?
No. When a POST request is made for a ModelResource it is routed to obj_update and then to obj_create unless it is overridden. There it takes value from **kwargs and creates a model entry. In your case it's not there thus taking empty strings.
For reference, look at the documentation:
https://django-tastypie.readthedocs.io/en/latest/non_orm_data_sources.html
I'm having a problem with django. I created my model with some fields like so:
class MyModel(Model):
field1 = models.IntegerField()
field2 = models.CharField(max_length=200)
field3 = models.IntegerField()
class Meta:
unique_together = (('field1', 'field2'))
And I have an API route, which links to a model serializer:
class MyModelSerializer(ModelSerializer):
class Meta:
model = models.MyModel
The problem is that when I send objects via the API to update them, the serializer raises {'non_field_errors': ['The fields field1, field2 must make a unique set.']}
Does anyone know how to prevent UniqueTogetherValidator to be used on the serializer?
This is based off of djangorestframework==3.8.2.
Override the get_validators method that is on ModelSerializer and comment out the call for getting the unique_together_validators if that is the only validator you really care about omitting. Otherwise this answer https://stackoverflow.com/a/46022282/1658745 suggests just settings validators to empty list to turn off all validation for that serializer.
class MyModelSerializer(ModelSerializer):
def get_validators(self):
"""
Determine the set of validators to use when instantiating serializer.
"""
# If the validators have been declared explicitly then use that.
validators = getattr(getattr(self, 'Meta', None), 'validators', None)
if validators is not None:
return validators[:]
# Otherwise use the default set of validators.
return (
# self.get_unique_together_validators() +
self.get_unique_for_date_validators()
)
class Meta:
model = models.MyModel
I'm having a strange riddle to solve:
I extended my django-1.4 user-objects with a UserProfile, as described at https://docs.djangoproject.com/en/dev/topics/auth/ and wanted to implement project-specific roles. So my models look like the following:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
projects = models.ManyToManyField(Project, through='UserProjectRole')
[...]
class UserProjectRole(models.Model):
userProfile = models.ForeignKey(UserProfile)
project = models.ForeignKey(Project)
group = models.ForeignKey(Group)
[...]
I needed to pass a css-class, so I created a model-form for UserProjectRole and implemented the userProfile-Field with a widget:
class ProjectRoleForm(forms.ModelForm):
userProfile = forms.ModelMultipleChoiceField(label='Users',
queryset=UserProfile.objects.all(),
widget=forms.SelectMultiple(attrs={'class': 'select-multiple'}))
class Meta:
model = UserProjectRole
The form is presented correctly, however, it's crashing during save-process with the following error
Cannot assign "[<UserProfile: MyUser>]": "UserProjectRole.userProfile" must be a "UserProfile" instance.
Does anyone have an idea?
My guess is it's because you are using a forms.SelectMultiple widget. Which gives you a list of UserProfile instances ( [<UserProfile: MyUser>] ) and not a single UserProfile instance which is of course required to set on a ForeignKey field (UserProjectRole.userProfile). Thus I suggest to try using a forms.Select widget instead.
I have a model form that I use to update a model.
class Turtle(models.Model):
name = models.CharField(max_length=50, blank=False)
description = models.TextField(blank=True)
class TurtleForm(forms.ModelForm):
class Meta:
model = Turtle
Sometimes I don't need to update the entire model, but only want to update one of the fields. So when I POST the form only has information for the description. When I do that the model never saves because it thinks that the name is being blanked out while my intent is that the name not change and just be used from the model.
turtle_form = TurtleForm(request.POST, instance=object)
if turtle_form.is_valid():
turtle_form.save()
Is there any way to make this happen? Thanks!
Only use specified fields:
class FirstModelForm(forms.ModelForm):
class Meta:
model = TheModel
fields = ('title',)
def clean_title(self....
See http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#controlling-which-fields-are-used-with-fields-and-exclude
It is common to use different ModelForms for a model in different views, when you need different features. So creating another form for the model that uses the same behaviour (say clean_<fieldname> methods etc.) use:
class SecondModelForm(FirstModelForm):
class Meta:
model = TheModel
fields = ('title', 'description')
If you don't want to update a field, remove it from the form via the Meta exclude tuple:
class Meta:
exclude = ('title',)