I have following serializer:
class TrackGroupSerializer(serializers.ModelSerializer):
class Meta:
model = TrackGroup
fields = ('id', 'name', 'report', 'tracks') # `report` is FK
I am taking report id from url, so I thougth that this will work:
...
track_group = TrackGroup(report=report)
serializer = TrackGroupSerializer(
instance=track_group,
context=dict(request=request),
data=request.data
)
if serializer.is_valid():
...
This doesn't work because serializer has error for field report as the field is missing and is required. What is a correct way to provide data like report and have it still listed in TrackGroupSerializer fields as this serializer is used to return all data in response.
Thanks
Set the required flag to False in model serializer
class TrackGroupSerializer(serializers.ModelSerializer):
report = serializers.CharField(required=False)
class Meta:
model = TrackGroup
fields = ('report', ...)
In case you want to create a serializer and save a model instance without providing value to a variable, you can always set a default value to it in the model.
In models.py
class TrackGroup(models.Model):
report = models.CharField(default = '-')
You can set data as a dict with all keys not as request.data like so
data = {'report': report.id, **request.data}
serializer = TrackGroupSerializer(
instance=track_group,
context=dict(request=request),
data=data
)
The correct solution seems to be partial=True:
serializer = TrackGroupSerializer(
instance=track_group,
context=dict(request=request),
data=request.data,
partial=True
)
It seems to be cleaner than modifying request data.
Related
I'm using Django 2.x and DRF.
I have a model to save rating for each entity
class UserRideRating(models.Model):
user_ride = models.OneToOneField(UserRide, on_delete=models.CASCADE, related_name='user_ride_rating')
rating = models.PositiveIntegerField(
validators=[validate_rating_range]
)
and serializers.py
class UserRideRatingSerializer(serializers.ModelSerializer):
class Meta:
model = UserRideRating
fields = ('id', 'user_ride', 'rating')
I have a view to create rating object if not already exists or update the rating if already exists. Also, I want to check validation using default Serializer validation and thus my view is like
#api_view(['POST'])
def rate(request):
serializer = UserRideRatingSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
# create or update data
return Response(serializer.data)
When on passing data from the postman, it gives an error as
{
"user_ride": [
"This field must be unique."
]
}
How can I enable check for only valid data and not the uniqueness of the field? Although user_ride should be unique, it can pass in the request data.
You have to supply the serializer with the existing instance of UserRideRating if you want to skip this unique check.
Since you are not supplying the serializer with the instance it is considering you are creating a new one with the same UserRide id.
Use this code:
#api_view(['POST'])
def rate(request):
user_ride_id = request.data.get('user_ride')
try:
instance = UserRideRating.objects.get(user_ride_id=user_ride_id)
except UserRideRating.DoesNotExist:
instance=None
serializer = UserRideRatingSerializer(instance, data=request.data)
if serializer.is_valid(raise_exception=True):
# create or update data
return Response(serializer.data)
Many time we access data via serializer directory according to relationship defined in models in Django(1.11.10). How can i set a filter like fetch-only is_active=1.
class DaasJobsSerializer(serializers.ModelSerializer):
class Meta:
model = DaasJobs
fields = '__all__'
class DaasScheduleSerializer(serializers.ModelSerializer):
jobs = DaasJobsSerializer(read_only=True,many=True)
class Meta:
model = DaasSchedule
fields = '__all__'
Here i just want to set a filter to fetch only those Jobs which db field is_active=1 in this line like that DaasJobsSerializer(read_only=True,many=True, filter={"is_active":1}) how to do something like this ??
Currently it is giving me all the data without checking is_active,
and i dont want to create serializerMethodField for that.. because all methods written earlier.. i am just setting a is_active field later in the tables in db.
If you want to do it via serializers you can try overriding the ListSerializer and passing it as a custom list_serializer_class.
class IsActiveListSerializer(serializers.ListSerializer):
def to_representation(self, data):
data = data.filter(is_active=1)
return super().to_representation(data)
In your serializer:
class DaasJobsSerializer(serializers.ModelSerializer):
class Meta:
model = DaasJobs
fields = '__all__'
list_serializer_class = IsActiveListSerializer # import it here
Of course this is a specific use-case, you could make a more generalized version of the ListSerializer to:
class FilteredListSerializer(serializers.ListSerializer):
filter_kwargs = {}
def to_representation(self, data):
if not self.filter_kwargs or not isinstance(self.filter_kwargs, dict):
raise TypeError(_('Invalid Attribute Type: `filter_kwargs` must be a of type `dict`.'))
data = data.filter(**self.filter_kwargs)
return super().to_representation(data)
And then you could sub-class that to make other specific ListSerializers such as:
class IsActiveListSerializer(FilteredListSerializer):
filter_kwargs = {'is_active': 1}
and many others...
I have my users account authority stored in request.session.get('authority')
At the moment the endpoint in DRFs web-browsable HTML representation is showing all the addresses of all accounts in the html form. Which I'd expect as I'm querying all the addresses.
Screenshot of DRF form: addresses are displaying their authority.uuid, they should only show the current authorities address data.
Company Serializer
# ToDo: filter queryset objects
class CompanySerializer(serializers.ModelSerializer):
clients = serializers.HyperlinkedRelatedField(
many=True,
view_name='client-detail',
queryset=Client.objects.all()
)
addresses = serializers.HyperlinkedRelatedField(
many=True,
view_name='address-detail',
queryset=Address.objects.all()
)
class Meta:
model = Company
fields = ('name', 'url', 'clients', 'addresses')
read_only_fields = ('authority',)
I want to be able to do something like:
addresses = serializers.HyperlinkedRelatedField(
many=True,
view_name='address-detail',
queryset=Address.objects.filter(authority=request.session.get('authority'))
)
But not sure there is a way to access the request data in the serializer when I'm setting up the HyperlinkedRelatedField.
Perhaps I'm approaching this in entirely the wrong way. Any guidance will be greatly appreciated.
Update
Many thanks to Enthusiast Martin, based on his answer this is how I've implemented it for now:
def hyperlinkedRelatedFieldByAuthority(model, view_name, authority):
return serializers.HyperlinkedRelatedField(
many=True,
view_name=view_name,
queryset=model.objects.filter(authority=authority)
)
class CompanySerializer(serializers.ModelSerializer):
def get_fields(self):
fields = super().get_fields()
authority = self.context['request'].session.get('authority')
fields['clients'] = hyperlinkedRelatedFieldByAuthority(Client, 'client-detail', authority)
fields['addresses'] = hyperlinkedRelatedFieldByAuthority(Address, 'address-detail', authority)
return fields
You can override serializer's get_fields method and update the addresses queryset.
You can access the request via serializer's context
Something like this:
def get_fields(self):
fields = super().get_fields()
request = self.context['request']
fields['addresses'].queryset = ** your queryset using request data **
return fields
TL;DR: What could be the reason the incoming data for one of my serializers does not get processed?
I'm working on a serializer for a nested relationship. The serializer should get a list of UUIDs, so that I can make many to many relationships. Here is the model:
class Order(
UniversallyUniqueIdentifiable,
SoftDeletableModel,
TimeStampedModel,
models.Model
):
menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
custom_choice_items = models.ManyToManyField(CustomChoiceItem, blank=True)
price = models.ForeignKey(MenuItemPrice, on_delete=models.CASCADE)
amount = models.PositiveSmallIntegerField(
validators=[MinValueValidator(MINIMUM_ORDER_AMOUNT)]
)
Here is the data (my post body) with which I hit the route in my tests:
data = {
"checkin_uuid": self.checkin.uuid,
"custom_choice_items": [],
"menu_item": self.menu_item.uuid,
"price": self.menu_item_price.uuid,
"amount": ORDER_AMOUNT,
}
response = self.client.post(self.order_consumer_create_url, self.data)
Note that the empty list for custom_choice_items does not change anything. Even if I fill it with values the same error occurs. And last but not least here are the serializers:
class CustomChoiceItemUUIDSerializer(serializers.ModelSerializer):
"""Serializer just for the uuids, which is used when creating orders."""
class Meta:
model = CustomChoiceItem
fields = ["uuid"]
....
# The serializer that does not work
class OrderSerializer(serializers.ModelSerializer):
menu_item = serializers.UUIDField(source="menu_item.uuid")
custom_choice_items = CustomChoiceItemUUIDSerializer()
price = serializers.UUIDField(source="price.uuid")
wish = serializers.CharField(required=False)
class Meta:
model = Order
fields = [
"uuid",
"menu_item",
"custom_choice_items",
"price",
"amount",
"wish",
]
The problem is now, that when I leave out many=True, I get the error:
{'custom_choice_items': [ErrorDetail(string='This field is required.', code='required')]}
And If I set many=True I just simply don't get any data. By that I mean e.g. the value of validated_data["custom_choice_items"] in the serializers create() method is just empty.
What goes wrong here?
I even checked that the data is in the request self.context["request"].data includes a key custom_choice_items the way I pass the data to this view!
EDIT: Here is the data I pass to custom_choice_items:
data = {
“checkin_uuid”: self.checkin.uuid,
“custom_choice_items”: [{“uuid”: custom_choice_item.uuid}],
“menu_item”: self.menu_item.uuid,
“price”: self.menu_item_price.uuid,
“amount”: ORDER_AMOUNT,
}
self.client.credentials(HTTP_AUTHORIZATION=“Token ” + self.token.key)
response = self.client.post(self.order_consumer_create_url, data)
When you post data using the test api client, if the data contains nested structure you should use format=json, like this:
response = self.client.post(self.order_consumer_create_url, data, format='json')
Did you override .create method in the serializer? Something like this should work:
from django.db import transaction
class OrderSerializer(serializers.ModelSerializer):
# your fields and Meta class here
#transaction.atomic
def create(self, validated_data):
custom_choice_items = validated_data.pop('custom_choice_items')
order = super().create(validated_data)
order.custom_choice_items.add(*custom_choice_items)
return order
By the way you don't really need to define CustomChoiceItemUUIDSerializer if is just the primary key of that.
I have a simple view with which I want to be able to set a nullable TimeField to None:
class Device(models.Model):
alarm_push = models.TimeField(null=True, blank=True)
class DeviceSettingsSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = ('alarm_push',)
class DeviceSettingsView(RetrieveUpdateAPIView):
serializer_class = DeviceSettingsSerializer
lookup_field = 'uuid'
def get_queryset(self):
return Device.objects.all()
But if I try to PATCH data like {'alarm_push': None} I get an error like {"alarm_push":["Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]]."]}
How can I set alarm_push to None?
As your Serializer is a ModelSerializer DRF will use a TimeField() for your alarm_push model attribute. When you checkout the sourcecode of the DRF Timefield https://github.com/encode/django-rest-framework/blob/master/rest_framework/fields.py#L1278 you can see that to_internal_value is raising your error when every attempt of parsing the value failes.
So to have your TimeField be empty you should patch {"alarm_push": ""} with an empty string to represent an empty state.