Drf: Simple Foreign Key Assignment with Nested Serializers

DRF: Simple foreign key assignment with nested serializers?

The best solution here is to use two different fields: one for reading and the other for writing. Without doing some heavy lifting, it is difficult to get what you are looking for in a single field.

The read-only field would be your nested serializer (ChildSerializer in this case) and it will allow you to get the same nested representation that you are expecting. Most people define this as just child, because they already have their front-end written by this point and changing it would cause problems.

The write-only field would be a PrimaryKeyRelatedField, which is what you would typically use for assigning objects based on their primary key. This does not have to be write-only, especially if you are trying to go for symmetry between what is received and what is sent, but it sounds like that might suit you best. This field should have a source set to the foreign key field (child in this example) so it assigns it properly on creation and updating.


This has been brought up on the discussion group a few times, and I think this is still the best solution. Thanks to Sven Maurer for pointing it out.

DRF create object with nested serializers and foreign keys

So in the end I solved this by using RelatedField instead of separate serializers for every foreign key, with the exception of the nested EventSerializer that is really required to write nested Event objects.

Here's the RaceSerializer:

class RaceSerializer(serializers.ModelSerializer):
owner = UserSerializer(read_only=True)
type = TypeField()
country = CountryField()
region = RegionField()
timezone = TimezoneField()
events = EventSerializer(many=True)
race_cal_types = serializers.SerializerMethodField()

def create(self, validated_data):
with transaction.atomic():
events = validated_data.pop('events', None)
race = Race(**validated_data)
race.save()
for event in events:
Event.objects.create(race=race, **event)
return race

And here is the combo of RelatedField and ModelSerializer I'm using for each field in my RaceSerializer, e.g. for the region foreign key:

class RegionSerializer(serializers.ModelSerializer):

class Meta:
model = Region
fields = ('pk', 'name', 'code')

class RegionField(RelatedField):

def get_queryset(self):
return Region.objects.all()

def to_internal_value(self, data):
return self.get_queryset().get(**data)

def to_representation(self, value):
return RegionSerializer(value).data

Each field (type, region, country, timezone) has its own to_internal_value and to_representation methods to serializer/deserialize data the way I need to.

Nested serializer using foreign key with only one object in DRF

I would suggest for you to use two different fields for this.

Something like this:

class ServiceSerializer(serializers.ModelSerializer):

messages = MessageSerializer(many=True, read_only=True)
message = MessageSerializer(write_only=True)

So, for input - you will have only write_only attribute which won't be serialized on the way out - only when you post ( you can decide with required attribute to make it optional )

And vice versa for messages - only read only.

And you handle it accordingly in create method.

Assigning current 'User' as foreign key to nested serializers

You need to remove user from fields of UserAddressSerializer:

class UserAddressSerializer(serializers.ModelSerializer):

class Meta:
model = UserAddress
fields = ('address_1', 'address_2', 'country', # <-- Here
'state_province', 'city', 'zip_code')

[DRF]What are the benefits of using nested Serializers over separate resources?

In general, I use nested serializers when I want an interface where model B is always read (or created) together with model A. An example of this would be a Pizza model with a many-to-many relationship to Topping.

You'd want to separate these into two models to achieve a normalized database model, but retrieving a pizza's information without its toppings wouldn't make sense.

A 'create' example might be UserCredentials and Profile. You might want to separate these for security reasons, but it wouldn't make sense to create UserCredentials without an attached Profile.

The advantage here is a less complex interface. On the client-side, you don't need to do complex chained requests. (Retrieve pizza, then retrieve toppings every single time.) It also reduces overhead on the server, because there is only one request.

There are some disadvantages though. I tend to steer away from nested serializers because:

  • When you also want to be able to retrieve/create these relationships separately, you'll need either two serializers, or one more complex serializer. This requires a lot of maintenance to keep everything working correctly.
  • You might end up with multiple layers of nested serializers, which make it harder to separate your API into clearly defined resources.
  • Performance is harder to control, because you can't apply pagination on the nested resource.

DRF - Relate an object to another using views, serializers and foreign key

The problem is with the CurrentAccountSerializer serializer, you set the customer_account field as read only. You should also use two serializers with CurrentAccount, the first to list existing instances and the second to create new ones.

serializers

class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ('id', 'first_name', 'last_name')

class CurrentAccountInfoSerializer(serializers.ModelSerializer):
customer_account = CustomerSerializer(read_only=True)

class Meta:
model = CurrentAccount
fields = ('id', 'customer_account', 'balance')

class CurrentAccountCreationSerializer(serializers.ModelSerializer):
customer_account = serializers.PrimaryKeyRelatedField(queryset=Customer.objests.all())

class Meta:
model = CurrentAccount
fields = ('id', 'customer_account', 'balance')

def create(self, validated_data):
return CurrentAccount.objects.create(**validated_data)

views

@api_view(['GET', 'POST'])
def currentaccount_list_view(request):
if request.method == 'GET':
try:
data = CurrentAccount.objects.all()
ser = CurrentAccountInfoSerializer(data, many=True)
return response_success(data=ser.data)
except Exception as e:
return response_failed(e.args)

elif request.method == 'POST':
try:
ser = CurrentAccountCreationSerializer(data=request.data, context={'request': request})
if ser.is_valid():
instance = ser.save()
return response_success(data=CurrentAccountInfoSerializer(instance=instance).data)
else:
return response_failed('Something wrong with the input data')
except Exception as e:
return response_failed(e.args)


Related Topics



Leave a reply



Submit