I'm using Django 2.2 and Django REST framework.
In the serializer, I want to have few fields only for creating an instance and not for updating. Means, the user won't be able to change the value of that field once created.
Since Django does not provide any default way to do it. I am writing a mixin which when used will use the create_only_fields from the serializer Meta to remove the fields from the request data with PUT/PATCH request.
serializer_mixin.py
class CreateOnlyFieldsMixin(object):
def to_internal_value(self, data):
data = super().to_internal_value(data)
print(data)
if self.instance:
# Update request
print(data)
for x in self.Meta.create_only_fields:
if x in data:
data.pop(x)
return data
and using in the serializer like
class MySerializer(CreateOnlyFieldsMixin, serializers.ModelSerializer):
class Meta:
model = MyModel
fields = [
'id',
'name',
'value'
]
create_only_fields = ['name']
But now calling any endpoint, it gives an error
Cannot call `.is_valid()` as no `data=` keyword argument was passed when instantiating the serializer instance.
Putting CreateOnlyFieldsMixin after serializers.ModelSerializer gives no error but has no effect of the mixin.
Appending to_internal_value directly in the serializer is working as expected.
Related
My model has a type attribute and get_required_fields method.
get_required_fields returns a list of required fields based on self.type which I then use in full_clean method to validate the model before saved
for field in self.get_required_fields():
if getattr(self, field, None) is None:
missing_fields.append(field) # todo append verbose_name
if missing_fields:
raise ValidationError({x: "Povinné pole pre daný druh" for x in missing_fields})
The problem is that this doesn't work for DRF model serializers. I've overriden validate method this way:
def validate(self, attrs):
Income(**attrs).full_clean()
return super().validate(attrs)
But then I realized this will not work for PATCH method and probably also not for POST with missing fields
How can I make it work for both model and DRF ModelSerializer while using the same code to keep it DRY?
In django, I was able to pass data using dictionary. Like I set the objects in my dictionary and pass it in return render and call the object in frontend (return render(request, 'c.html', context) right? so How can I do this in django rest?
You may return Response in rest framework like this if you are using django rest framework.
context = {'key':'value'}
return Response(context)
Or if you are using a serializer then
return Response(serializer.data)
In Django REST Framework the concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some field that is going to be processed. For example, if you have a class with the name Employee and its fields as Employee_id, Employee_name, is_admin, etc. Then, you would need AutoField, CharField, and BooleanField for storing and manipulating data through Django. Similarly, serializer also works with the same principle and has fields that are used to create a serializer.
DictField is basically a dictionary field that validates the input against a dictionary of objects. It has the following arguments:
child and allow_empty like this>>>
field_name = serializers.DictField(*args, **kwargs)
for example document = DictField(child=CharField())
you can use serializer like below>>>
from rest_framework import serializer
class Any(object):
def __init__(self, dictonary):
self.dict = dictionary
class AnySerializer(serializers.Serializer):
dictionary = serializers.DictField(
child = serializers.CharField())
you can visit similar problem for understanding through the real problem.
And this link is the complete documentation of your problem. You can check this out.
Using latest version of Django and DRF.
I have a rather complex requirement I can't find a solution for. I'll try to simplify it.
Let's say I have a model that has two fields. field_a and field_b
I have a ModelSerializer for it. I POST a request with its fields. The fields get validated with the model and then against my two serializer functions validate_field_a and validate_field_b. All is well.
Now I'd like my POST request to include a third field that is not a member of that model. let's call it field_c. I have a custom def create(self, validated_data): in my serializer which saves everything to the database.
with regards to field_c I would like to:
Custom Validate it. just like I do with the other two fields.
Require that it is mandatory for the whole request to succeed and if it's not, issue a "Field is required" error just like if I forgot to POST one of my required model fields.
Have the chance to take field_c and save it onto a totally different unrelated Model's row in the db.
I can't seem to get around that. If I add field_c to the fields meta - it throws an exception saying justifiably that field_c is not in my model. If I don't include it in fields, the validate_field_c which I really want to put there doesn't even get called.
What can I do?
You can add the custom field in your serializer as a write_only field and override the create method so that you can handle the custom field's value.
Something like this:
class MySerializer(serializers.ModelSerializer):
field_c = serializers.CharField(write_only=True)
class Meta:
model = MyModel
fields = ('field_a', 'field_b', 'field_c')
def validate_field_c(self, value):
if value is 'test':
raise ValidationError('Invalid')
return value
def create(self, validated_data, **kwargs):
field_c = validated_data.pop('field_c')
return MyModel.objects.create(**validated_data)
Don't use ModelSerializer for this - use a serializer that recreates the same fields as your model & include field_c as you would.
I understand that you want your model to do some of the work in the validation process but the design of DRF is such that it isolates these responsibilities. You can read more about it here. Basically, the serializer should be the one doing all the validation heavy-lifting.
Of course, this means that you'll have to explicitly define the validation methods in the serializer.
In your custom create() method you can create the model instance or do whatever you want in it as required.
I am having an issue with serialization. I have a queryset of objects e.g.:
uvs = UserVehicles.objects.all()
Some of those objects are expired, some are not. I would like to have different fields in serializer, depending on expiry information. For example, I would like to exclude status and distance_travelled fields from expired objects. What is the easiest way to achieve that? I tried with the next code, but self.object in init method contains an array, so it would remove fields for all objects, not just expired ones.
serialized_data = UserVehicleSerializer(uvs, many=True).data
class UserVehicleSerializer(serializers.ModelSerializer):
class Meta:
model = UserVehicle
fields = ('type', 'model', 'status', 'distance_travelled',)
def __init__(self, *args, **kwargs):
super(UserVehicleSerializer, self).__init__(*args, **kwargs)
if self.object.is_expired:
restricted = set(('distance_travelled', 'status',))
for field_name in restricted:
self.fields.pop(field_name)
I would suggest keeping the business logic out of the serializer. you could create a separate serializer for expired vehicles/objects and a separate serializer for active vehicles and choose the correct serializer in the view. That way, if your business logic goes in different directions for each type of vehicle , it should be easy to manage.
You could do that in the Serializer's to_representation().
http://www.django-rest-framework.org/api-guide/fields/#custom-fields has examples for Fields but Serializers do inherit from Field.
Just call the parent's to_representation and remove the fields you don't want.
I'm following the tutorial laid out here to create generic class based views for my API - however, I have run into a small problem. I would like to update the model behind the view partially. I used to be able to do this by using the partial property when I created the serializer. However, it seems that once I start using generic class based views I lose the ability to set whether or not I can allow partial updates to the model. How can I override the partial property of a ModelSerializer? My code is quite simple:
class DejavuUserDetail(generics.RetrieveUpdateAPIView):
'''
Get a user or update a user
'''
lookup_field = "email"
queryset = DejavuUser.objects.all()
serializer_class = UserSerializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = DejavuUser
partial = True
def restore_object(self, attrs, instance=None):
"""
Given a dictionary of deserialized field values, either update
an existing model instance, or create a new model instance.
"""
if instance is not None:
#set the required fields and return the instance
I'm trying to access the API via PUT
For partial updates use PATCH.
Also note that partial isn't an option on the serializer metaclass, but instead set on instantiating the serializer.