17

My design is as following about Django ModelSerializer. There are model A and model B. Model B has a foreign key field of Model A. For some reasons, I can not use the primary key directly to serialize Model B. As my thought, what I need is to serialize two other fields(unique together in Model A).

And I see the SlugRelatedField must be used for one slug field. I searched there is a NaturalKeyField can support NaturalKeyField. But it looks like it is superseeded by django-rest-framework. But I checked the django-rest-framework, there is no such field at all. Can anyone help?? What should I do?

The code is as following. Model A

class AssetModel(models.Model):
 org = models.ForeignKey(Org, related_name='models')
 name = models.CharField(max_length=128)
 model_type = models.SmallIntegerField(default = 3,choices = MODEL_TYPE )
 directory = models.CharField(max_length = 128)
 ...
 class Meta:
 unique_together = ('org', 'name',)

Model B

class Dataitem(models.Model):
 mod = models.ForeignKey(AssetModel, related_name='dataitems')
 name = models.CharField(max_length=128)
 data_type = models.SmallIntegerField(default =0,choices = DATAITEM_DATATYPE)
 ...

Serializer of model A

class AssetModelSerializer(serializers.ModelSerializer):
 org = serializers.SlugRelatedField(queryset=Org.objects.all(), slug_field='name')
 class Meta:
 model = AssetModel
 fields = ('org', 'name', 'model_type',..

Serializer of model B

class DataitemSerializer(serializers.ModelSerializer):
 class Meta:
 model = Dataitem
 fields = ('mod', 'name','data_type'...)

The primary key of Model A is just a id Django auto added. When serialize the model B, I need to get the org and name of model A. Both read and write are needed.

Paolo
21.4k21 gold badges78 silver badges124 bronze badges
asked Jan 14, 2016 at 8:41
2
  • github.com/tomchristie/django-serializers Commented Jan 14, 2016 at 8:42
  • 1
    I think the better question would be to post your models and explain what you want to achieve, instead of asking about a solution you thought of Commented Jan 14, 2016 at 8:49

2 Answers 2

13

Nested Serializer

You can do something like this, define a serializer for Dataitem that can reuse a serializer of the AssetModel model

class AssetModelSerializer(serializers.ModelSerializer):
 class Meta:
 model = AssetModel
 # Fields org and name of AssetModel will be inlcuded by default
class DataitemSerializer(serializers.ModelSerializer):
 class Meta:
 model = Dataitem
 mod = AssetModelSerializer()
 # This is the Dataitem.mod field
 # which is a FK to AssetModel,
 # Now it'll be serilized using the AssetModelSerializer
 # and include the org and name fields of AssetModelSerializer

I prefer this approach because of the control I get. If you serialize using the above you get a structure like this:

data_item = {'name': ..., 'mod': {'org': ..., 'name': ...}}
 ^
 |___ AssetModel fields

Alternatively you can also use depth = n

You can also use depth = 1 in Dataitem

class DataitemSerializer(serializers.ModelSerializer):
 class Meta:
 model = Dataitem
 depth = 1 # Will include fields from related models
 # e.g. the mod FK to AssetModel

Writable Nested Serializer

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.

We have to implement create/update to make this writable as per DRF's documentation

class DataitemSerializer(serializers.ModelSerializer):
 class Meta:
 model = Dataitem
 # Nested serializer
 mod = AssetModelSerializer()
 # Custom create()
 def create(self, validated_data):
 # First we create 'mod' data for the AssetModel
 mod_data = validated_data.pop('mod')
 asset_model = AssetModel.objects.create(**mod_data)
 # Now we create the Dataitem and set the Dataitem.mod FK
 dataitem = Dataitem.objects.create(mod=asset_model, **validated_data)
 # Return a Dataitem instance
 return dataitem
answered Jan 14, 2016 at 9:10
Sign up to request clarification or add additional context in comments.

5 Comments

There are other fields in AssetModel, which I do not want to serialize them. To use nested serializer, do I have to define a new serializer only includes org and name fields?
Yes simply use fields = (...) like you did
thanks a lot. But it looks like the write on the mod fields would fail.
No problem! I'd try get a recent version of DRF and read this about writable nested serializers django-rest-framework.org/api-guide/serializers/…
@ThomasLiu You're welcome. I've added an example so you can relate to your models. Be aware that this example is for 1 nested model, but if AssetModel itself needs a FK key you will have to unpack more data from validated_data and create the object, link the FK etc. I suggest you practice this with a simple 1 nested model first. I Hope this puts you on the right track. Please upvote/accept the answer if it helped you! Thank you!
11

There seems to be a library that does this drf-writable-nested

it handles the creation and serialisation of these types

  • OneToOne (direct/reverse)
  • ForeignKey (direct/reverse)
  • ManyToMany (direct/reverse excluding m2m relations with through model)
  • GenericRelation (this is always only reverse)
answered Oct 26, 2017 at 3:10

1 Comment

Just got to try it out, is_valid acts funny for now but in general it does work!! And is really easy to try. This answer should be bumped up

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.