Search code examples
pythondjangodjango-modelsdjango-rest-frameworkdjango-serializer

how to create three tables in nested-serializer objects using django rest framework


I am trying to create a three model nested objects in Django Rest Framework.

models.py:

class Project(models.Model):
    project_id = models.AutoField(primary_key=True, unique=True)
    project_name = models.CharField(max_length=100)

class ProjectSite(models.Model):
    site_id = models.AutoField(primary_key=True, unique=True)
    site_name = models.CharField(max_length=200,name='project_site_name')
    project_id = models.ForeignKey(Project, on_delete=models.CASCADE, blank=True, null=True, 
    related_name="projectid")

class Assignment(models.Model):
    assignment_id = models.AutoField(primary_key=True)
    assignment_name = models.CharField(max_length=150)
    site_id = models.ForeignKey(ProjectSite,related_name="projectsiteidkey", on_delete=models.CASCADE)
    assigned_to_id = models.ForeignKey('auth.User',related_name="assignedtoidfkey",on_delete=models.CASCADE)

serializer.py:

class ProjectSerializer(serializers.ModelSerializer):
    
    class Meta:
        model = Project
        fields = '__all__'
class ProjectSiteSerializer(serializers.ModelSerializer):
  
    class Meta:
        model = ProjectSite
        fields = '__all__'

class AssignedUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = Assignment
        fields = '__all__'

class CreateNewProjetSerial(serializers.ModelSerializer):
    site_name = ProjectSiteSerializer(many=True)
    assigned_to_id = AssignedUserSerializer(many=True)
    class Meta:
        model = Project
        fields = ['site_name','project_name','assigned_to_id']
   
    def create(self, validated_data):
        site_name = validated_data.pop('site_name')
        assigned_to_id = validated_data.pop('assigned_to_id')
        
        projects = Project.objects.create(**validated_data)
  
        for i in site_name:
            ProjectSite.objects.create(site_name=projects, **i)
            
            
        for j in assigned_to_id:    
            Assignment.objects.create(assigned_to_id=projects, **j)
  
        return projects 

view.py:

class MultieCreateAPIView(generics.CreateAPIView):
  queryset = Project.objects.all()
  serializer_class = CreateNewProjetSerial

I would like to post a JSON object like this:

{
"site_name": [{"site_name": "site1"}, {"site_name": "site2"}],
"project_name": "test_project",
"assigned_to_id": [{"assigned_to_id":2}, {"assigned_to_id":3}]
}

When I try to post this I got error with "Cannot assign "<Project: test_project>": "Assignment.assigned_to_id" must be a "User" instance".


Solution

  • First, you don't need to set the primary_key fields because django will do that for you. So you'd better remove project_id, site_id, assignment_id field from the 3 models respectively. And some errors exist in the CreateNewProjetSerial. site_name, assigned_to_id field should be read_only, and the additional fields are required to receive your payload if you can't upload them with the related object.

    class CreateNewProjetSerial(serializers.ModelSerializer):
        site_name = ProjectSiteSerializer(many=True, read_only = True)
        assigned_to_id = AssignedUserSerializer(many=True, read_only = True)
        site_names = serializers.ListField(
            child = serializers.CharField(), write_only = True)
        user_ids = serializers.ListField(
            child = serializers.IntegerField(), write_only = True)
    
        class Meta:
            model = Project
            fields = ['site_name','project_name','assigned_to_id', 'site_names', 'user_ids']
       
        def create(self, validated_data):
            site_names = validated_data.pop('site_names')
            user_ids = validated_data.pop('user_ids')
            
            new_project = Project.objects.create(**validated_data)
      
            for site_name in site_names:
                new_project_site = ProjectSite.objects.create(project_id=new_project, site_name=site_name)            
                
                for user_id in user_ids:    
                    Assignment.objects.create(assigned_to_id__id=user_id, site_id=new_project_site)
      
            return new_project
    

    And in frontend, please upload like the following.

    {
        "site_names": ["site1", "site2"],
        "project_name": "test_project",
        "user_ids": [2, 3]
    }