I receive a number of similar objects as an input for my API, and I deserialize them using my own serializer with the parameter many=True like this:
serializer = MySerializer(data=request.data, many=True)
The serializer is an instance of ListSerializer.
Then I need to make sure that there are certain combinations of objects in that list. However, I don't seem to find a way to write a .validate() method on the ListSerializer of replace it by my own ListSerializer implementation.
Is there a way to do this validation in the serializer, or do I have to iterate over the deserialized objects and check them?
The Django REST frameworks documentation has a section on customizing ListSerializer behavior.
This entails creating a custom subclass of ListSerializer. You would probably want to create some custom validation in your subclass.
Related
I have a JSONField in my model that stores some configuration data. I want to access this field (both read and write) with ability to make partial updates of inner fields and their values.
For purpose of example let a model be called MyModel with JSONField called config:
class MyModel(models.Model):
config = JSONField(default=dict())
...
I created a separate ViewSet to access information stored in config field. Assume that user model has ForeignKey relation to MyModel. Simplified version of this ViewSet is:
class ConfigurationFieldViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
serializer_class = MyModelConfigurationSerializer
def get_object(self):
return self.request.user.my_model
Data stored in config has a certain structure with several possible inner objects:
{
"C1": {"counter": 42, "active": false},
"C2": {"counter": 13, "active": true}
}
To access and correctly serialize MyModel instance at all levels of nesting I have created serializers for each level of field. To acces config field in MyModel itself I'm using this serializer:
class MyModelConfigurationSerializer(serializers.ModelSerializer):
configuration = ConfigurationFieldSerializer(required=True)
class Meta:
model = MyModel
fields = ('configuration',)
To access and serialize first layer of configuration field there's second serializer:
class ConfigurationFieldSerializer(serializers.Serializer):
C1 = BaseConfigurationSerializer(required=True)
C2 = BaseConfigurationSerializer(required=True)
At last to access inner structure of each C1 and C2 fields there's third serializer:
class BaseConfigurationSerializer(serializers.Serializer):
counter = serializers.IntegerField(
required=False,
help_text=_('Some integer field help text')
)
active = serializers.BooleanField(
required=False,
help_text=_('Some boolean field description')
)
The code above works perfectly to read data stored in config field and correctly serializes it's inner objects. The problem appears when I try to perform a PUT on this field.
If I override update method at the level of MyModelConfigurationSerializer, then serializers verify data I'm submitting but as a chunk and I'm only able to save it all at once. If I'm trying to submit some inner field I still correctly receive validation errors by inner serializers.
def update(self, instance, validated_data):
instance.configuration = validated_data.get(
'configuration', instance.configuration
)
instance.save()
return instance
What I'm unable to do though is call update methods of inner serializers (ConfigurationFieldSerializer and BaseConfigurationSerializer in this case): if I implement their update methods they simply do not get called.
According to DRF Documentation writable nested representations are possible and corresponding update or create methods should be called whenever update is called on serializer of top level.
I've had this problem recently as well, and it looks like the way you are doing it is the "only way" when it comes to nested writable serializers.
From the same DRF docs you've probably already seen:
Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default ModelSerializer .create() and .update() methods do not include support for writable nested representations.
There are however, third-party packages available such as DRF Writable Nested that support automatic writable nested representations.
Basically it means that when you have nesting it won't even try to call any of the nested serializer storage methods.
That may seem like a bit of a pain, but in retrospect it's probably better for the design of your application. Your example is pretty simple, but in other situations the ordering in which things are saved might be important. If update of each nested serializer was ran automatically then DRF would have to somehow know when to save each thing.
As an example, if your example was about create rather than update, it would mean that you need to first store your model MyModel before storing the configuration on top of it. However DRF cannot know that.
Also it could as easily have been that configuration was actually another related model which needed to be saved first before you could save a relation to it from MyModel. So DRF takes the route of just telling you to do it yourself, at the root serializer.
From my own experience this is also helpful to allow you to fine-tune performance later (ex. in your case you can avoid saving MyModel twice).
Finally, if you want to make your code more modular, you can still do it (send segments of the validated data to different handlers, eg to a new update_configurations() function), it just won't be done automatically using the nested serializers.
I am using Django and Django Rest Framework with the Serializer Extensions Mixin to expand fields. I have some calculated fields that I only want to call sometimes to minimize hits on my DB. However, I need to be able to call these calculations in both templates (i.e. through the models) and through the serializers (i.e. using DRF's serializers.MethodField + Serializer Extensions Mixin expand feature).
As it stands, the only way I can figure out how to do this is by including the logic in both models.py AND serializers.py, as I can't use serializers.MethodField to call the method that I created in models.py. Not very DRY and a huge potential flaw.
When I try to call the method via serializers.MethodField it simply returns the method object and doesn't run the method itself (i.e. "<property object at 0x7f18d78de9a8>").
Is there any way to force DRF to run a method that is in models.py only when triggered? If I include it as a serializers.ReadOnlyField, it will trigger every time the serializer is called, which I don't want. However, Serializer Extensions Mixin doesn't support serializers.ReadOnlyField.
I suppose I could make a serializer specifically for this instance, but that seems overly complicated.
Any ideas? Thank you in advance!
I would like to auto populate some fields in a Serializer (Not a ModelSerializer)
I need a method I can hook into, that gives me both access to the data passed in, and the ability to manipulate which fields are shown dynamically (but I can see how to do that from the example in the docs)
Essentially, how can I access and manipulate the data that has been passed into the Serializer at it's initialisation.
http://www.django-rest-framework.org/api-guide/serializers/#example
I believe the method you are looking for is .to_internal_value.
I have overwritten clean() methods for some of my models to construct constraints to meet my DB schema requirements (Because it required runtime information for those validations).
Since now I have finished most of the back-end side components(models, signals, ..) now I'm trying to write ModelForms for my models.
What I'm wondering is that, is there any relationship between the clean() method of model and clean() implementation on the form side?
If so and form's clean() calls model's clean() I won't have to rewrite my model - side clean() implementation and be able to avoid code redundancy.
Yes, ModelForm cleaning involves model cleaning. That's the idea with a ModelForm: there are a lot of useful defaults that can be determined by auto building a form object from a model.
I've discovered this clean chaining through personal experience, but to back it up I can reference the source.
On 1.8, ModelForms call the model instance full_clean method. On 1.7, it calls the clean method directly.
Form.full_clean()
def full_clean(self):
# ..... snip
self._clean_fields()
self._clean_form()
self._post_clean()
ModelForm._post_clean for 1.8
Model full_clean() calls clean() amongst other validation: https://docs.djangoproject.com/en/1.8/ref/models/instances/
self.instance.full_clean(exclude=exclude, validate_unique=False)
ModelForm._post_clean for 1.7
self.instance.clean()
According to Model.clean and ModelForms clean, I don't think there is any relationship between them.
These two clean has the same name but they did a different job.
Model.clean is used to validate the data you are going to store into database, and make sure the data is ok and can be stored into database.
This method should be used to provide custom model validation, and to modify attributes on your model if desired.
ModelForms clean, by my understanding, is to validate what the user has entered, and make sure they are ok.
You can override the clean() method on a model form to provide additional validation in the same way you can on a normal form.
And on a normal form, it's:
Implement a clean() method on your Form when you must add custom validation for fields that are interdependent.
And I think this one is also what you wanted:
Notice that we are talking about the clean() method on the form here, whereas earlier we were writing a clean() method on a field. It’s important to keep the field and form difference clear when working out where to validate things. Fields are single data points, forms are a collection of fields.
Does anyone can tell me if it's possible to create a Model class, with some model fields and some other fields taking their data from external data sources. The point is that I would like this model to be exploited the same way as another model by ModelForm for instance. I mean if I redefine "objects" Manager of the model by specifying the actions to get the datas for special fields (those who may not be linked to datas from the database), would the modelForm link the input with the fields not attached to the database ? Similar question about related objects. If I have a Model that has a relation with that special Model, can I get this Model instances through the classic way to get related objects (with both the classic model fields and the non-database fields) ?
Please tell me if I'm not clear, I'll reformulate.
Thanks.
EDIT: I tried to make a Model with custom fields, and then override the default Manager and its functions: all, get, ... to get objects like it would be with classical Model and Manager, it works. However, I don't use QuerySet, and it seems that the only way to get ModelForm, related objects and the admin functionnalities, working with it, is to build the QuerySet properly and let it being returned by the manager. That's why now I'm wondering if it's possible to properly and manually build a QuerySet with data got from external sources, or tell django-admin, model forms and related objects to take care of another class than queryset on this Model.
Thanks
The way is to define custom methods:
Define custom methods on a model to add custom "row-level"
functionality to your objects. Whereas Manager methods are intended to
do "table-wide" things, model methods should act on a particular model
instance.
This is a valuable technique for keeping business logic in one place
-- the model.
I have now a partial solution. I override the Manager and in particular its all() and get() functions (because I only need those functions for now). all() returns a queryset in which I added the result of some logics that give me objects build from external datas (taken through xmlrpc in my case). I added those objects to the qs through _result_cache attribute.
I think it's not clean and in fact my Model is now a custom Model and I don't have any database field. I may use it to fill database Models... However I can use it the same way as classic models: MyModel.objects.all() for example.
If anyone has another idea I'd really appreciate.
Regards