Swagger codegen with write_only PrimaryKeyRelatedField - django

In our project we have the following case more than once:
class TestSerializer(serializers.ModelSerializer):
unit = UnitSerializer(read_only=True)
unit_id = serializers.PrimaryKeyRelatedField(
queryset=Unit.objects.all(), source="unit", write_only=True)
This is according to our best practices where variables should describe which type they contain. In drf-yasg this is shown however as required in both the Read and the Write way. This means that code generators will mark this field as required, and when our API response comes in, they invalidate the request, because the unit_id is missing. Is there a way to declare this field as write_only other than this parameter?

Related

When and where is `Field.blank` checked by DRF?

I have a model
class SomeModel(models.Model):
emails = ArrayField(models.EmailField(), default=list)
And let's say I have the following Serializer of the model:
class SomeModelSerializer(serializers.ModelSerializer):
class Meta:
model = SomeModel
fields = ['emails']
The email field is not blank-able, i.e: It's required to set a value for it when submitting a Form of the model, or when making changes to its Admin page.
My understanding is that DRF relies as well on Django's internal machinery to validate whether emails is missing on the Serializer data or not. But the thing is that I can't find where (and when) this happens.
I've found that DRF is not calling the Model's clean() method anymore (link). But what baffles me is that changing the blank value on the field seems to have a direct impact on the Serializer. I have switched to blank=True, and then the Serializer would allow it to be saved without that field... Then I switched back to blank=False, and the Serializer would fail if emails is not present.
So do you have any idea of when and where DRF checks for a field's blank value?
Thanks!
As far as I know, it simply doesn't. Those are only used across forms and the django admin interface.
I always specify those things on the serializer level, by setting the appropiate arguments for my fields (doc), in this case it would be allow_blank.
I am building REST APIs with django, and the only case where the blank property on the model field catches me, is when fiddling around on the admin page.
However, there appears to be a package that could be of interest to you:
django-seriously.
I haven't used it, but it appears to call full_clean() on every save().
Of course, this has the disadvantage that you will probably loose DRFs nice error messages.

Django REST framework - sumbit only fields?

I use a model serializer in my code and need to get extra field value in the PUT request. This is to identify the user intention on certain operations. I see there are read_only and write_only option in serialize fields. But I can't use those since I'm not going to store this additional field to the model (it don't have such field).
How can I achieve this in the serializer correct way?
djangorestframework version 3.13.1
Found the solution based on the #loki's comment.
Included the serializer field as follows since I use it for PUT method only.
delete_now = serializers.BooleanField(default=False, write_only=True)

DRF PrimaryRelatedField when write and NestedSerializer when read?

I am using a nested serializer. I need ProfileSerializer to return full related Project object for get requests and consider only id switching (changing current) like with relatedPrimaryField behaiviour for post/put requests on ProfileSerializer. any solutions on how to achieve this ?
class ProfileSerializer(serializers.ModelSerializer):
current = ProjectSerializer()
class Meta:
model = Profile
fields = ('function', 'current')
As Linova mentioned, the easiest way to solve this issue without using a third-party library is to declare two separate fields in your serializer. Your nested serializer current would stay the same, but you would add a new PrimaryKeyRelatedField serializer. The nested serializer should be read only, but the related field would not be read only. I normally name the related field <field>_id by convention.
In GET requests, both the nested serializer and the id field will be returned, but for PUT or POST requests only the <field>_id needs to be specified.
class ProfileSerializer(serializers.ModelSerializer):
current = ProjectSerializer(read_only=True)
current_id = serializers.PrimaryKeyRelatedField(queryset=Projects.objects.all(), source='current')
class Meta:
model = Profile
fields = ('function', 'current', 'current_id')
The most consistent way I usually advice is to mark all the nested serializer (ProjectSerializer in this case) as read_only and add the id field as read_only=False
You'll therefore have consistence between the list/retrieve and creation/updates.

HyperlinkedRelatedField required=False not working

It seems no matter what I do I cannot get the parent field to not be required. I'm using DRF version 3.2.3 and Django 1.8.4.
Model definition of field:
parent = models.ForeignKey(
"self", verbose_name=_("Parent"), blank=True, null=True,
default=None, related_name='children')
The model also has a unique_together:
unique_together = (('owner', 'parent', 'name',),)
Serializer definition of field:
parent = serializers.HyperlinkedRelatedField(
view_name='category-detail', queryset=Category.objects.all(),
required=False)
I'm writing unittests and the response code is 400 with a text response of:
{'parent': [u'This field is required.']}
The parent field is a ForeignKey back to another row in the same table.
Gals/Guys any ideas how to fix this?
Sometimes a field can be implicitly made required by some other piece of code. One case I encountered is the model-level unique_together constraint, that makes all included fields required on the serializer level. From the doc:
Note: The UniqueTogetherValidation class always imposes an implicit constraint that all the fields it applies to are always
treated as required. Fields with default values are an exception to
this as they always supply a value even when omitted from user input.
I think you will just have to override the serializer save or viewset create/update to set the value to what you want at this point. Another option is to try to remove the UniqueTogetherValidator from the serializer's validators in its __init__. On the other hand I think it is added for a reason.
It is worth mentioning that in admin and anywhere else ModelForm is used, these fields won't be required because ModelForm is another thing entirely and it handles the validation in its own way.

Model datetime field validation for fields with auto_now

I am very new to django and python in general, and I was trying to learn rest_framework to create RESTful APIs.
So i have a model like this:
class Listing(models.Model):
listingid = models.BigIntegerField(primary_key=True)
sellerid = models.IntegerField()
createdon = models.DateTimeField(auto_now_add=True, editable=False)
expirydate = models.DateTimeField(null=True)
validationstatus = models.SmallIntegerField(default=0)
listingstatus = models.SmallIntegerField(
choices=((0, 'Active'),
(1, 'Hidden'),
(2, 'Suspended'),
(4, 'Expired'),
(5, 'Deleted'),
),
default=0)
Now i need to validate that the expirydate is always greater than the createdon date.
I know i can do this in the views, I guess that would not be a good idea, since now the validation only exists in the views.
So that leaves me with the serializers and the model.
I know I can override the save method to do check this like so:
class MasterListing(models.Model):
# fields here..
def save(self, *args, **kwargs):
if self.expirydate > self.createdon:
super().save(*args, **kwargs)
return ValidationError("Expiry date cannot be greater than created date ("++")")
but I dont know if this would be a good idea, since now I am raising an error which the programmer may forget to catch. I am also not sure if the fields would be populated when this method would run.
Another way I read about in the docs is the clean method which i couldn't really understand so well.
Can anyone guide me on how to handle situations like this when you are working with the rest_framework?
Some of the things I have read about validation till now:
Serializer Validation
Field level validation
Validators
Model Validation
override clean method
override save method
Just do it manually in the views
There seem to be so many options, and I might have even left a few, I could not clearly get an idea of when to use where.
I am sorry if this is a little on the beginner level, but i am new to frameworks and django seems to be very different from what i was doing in PHP. Any advice is welcome!
Edit: I will be using django for the rest_framework only and nothing else, since we only want to build RESTful APIs.
Django REST framework used to call Model.clean, which was previously the recommended place for putting validation logic that needed to be used in Django forms and DRF serializers. As of DRF 3.0, this is no longer the case and Model.clean will no longer be called during the validation cycle. With that change, there are now two possible places to put in custom validation logic that works on multiple fields.
If you are only using Django REST framework for validation, and you don't have any other areas where data needs to be manually validated (like a ModelForm, or in the Django admin), then you should look into Django REST framework's validation framework.
class MySerializer(serializers.ModelSerializer):
# ...
def validate(self, data):
# The keys can be missing in partial updates
if "expirydate" in data and "createdon" in data:
if data["expirydate"] < data["createdon"]:
raise serializers.ValidationError({
"expirydata": "Expiry date cannot be greater than created date",
})
return super(MySerializer, self).validate(data)
If you need to use Django REST framework in combination with a Django component that uses model-level validation (like the Django admin), you have two options.
Duplicate your logic in both Model.clean and Serializer.validate, violating the DRY principle and opening yourself up to future issues.
Do your validation in Model.save and hope that nothing strange happens later.
but I dont know if this would be a good idea, since now I am raising an error which the programmer may forget to catch.
I would venture to say that it would be better for the error to be raised than for the saved data to possibly become invalid on purpose. Once you start allowing invalid data, you have to put in checks anywhere the data is used to fix it. If you don't allow it to go into an invalid state, you don't run into that issue.
I am also not sure if the fields would be populated when this method would run.
You should be able to assume that if an object is going to be saved, the fields have already been populated with their values.
If you would like to both Model Validation and Serializer validation using Django REST Framework 3.0, you can force your serializer to use the Model validation like this (so you don't repeat yourself):
import rest_framework, django
from rest_framework import serializers
class MySerializer(serializers.ModelSerializer):
def validate(self, data):
for key, val in data.iteritems():
setattr(self.instance, key, val)
try:
self.instance.clean()
except django.core.exceptions.ValidationError as e:
raise rest_framework.exceptions.ValidationError(e.message_dict)
return data
I thought about generating a new function from my model's clean() function's code, and have it either spit out django.core.exceptions.ValidationError or rest_framework.exceptions.ValidationError, based on a parameter source (or something) to the function. Then I would call it from the model, and from the serializer. But that hardly seemed better to me.
If you want to make sure that your data is valid on the lowest level, use Model Validation (it should be run by the serializer class as well as by (model)form classes (eg. admin)).
If you want the validation to happen only in your API/forms put it in a serializer/form class. So the best place to put your validation should be Model.clean().
Validation should never actually happen in views, as they shouldn't get too bloated and the real business logic should be encapsulated in either models or forms.