I have three models, Student, Course and Homework:
What is happening is that I have a front end that the user inputs those fields in ie. all fields from Student
, Course
and Homework
and once those fields are filled out they suppose to click submit
to perform CREATE
operation. And that's triggers the CreatedData
function in views.py
.
I have found some relevant examples but still, my serializer is only returning the fields from the Student
model for CRUD
operation. I'm not able to get the Course
& Homework
models fields.
Return different serializer after create() in CreateAPIView in Django REST Framework
Django Rest API. Get all data from different models in a single API call
How to retrieve and create data from model via DJANGO REST API?
how to post multiple model data through one serializer in django rest api
models.py
class Student(models.Model):
student_id = models.UUIDField(default=uuid.uuid4, unique=True,
primary_key=True, editable=False)
firstName = models.CharField(max_length=20)
age = models.IntegerField(default=18)
class Course(models.Model):
student_id = models.ForeignKey(Student, on_delete=models.CASCADE)
courseName = models.CharField(max_length=20)
courseYear = models.IntegerField(default=2021)
student = models.ManyToManyField(Student, related_name='courses')
class Homework(models.Model):
student_id = models.ForeignKey(Student, on_delete=models.CASCADE)
hwName = models.CharField(max_length=20)
hwPossScore = models.IntegerField(default=100)
course = models.ForeignKey(Course, related_name='homeworks', on_delete=models.CASCADE, null=True, blank=True)
students = models.ManyToManyField(Student)
For these three models I have three serializer classes and combined one to get all data for one API call: Serializers.py
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = "__all__"
class HomeworkSerializer(serializers.ModelSerializer):
class Meta:
model = Homework
fields = __all__
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = "__all__"
###I combine both Student and Course into one
class Combined_Serializer(serializers.ModelSerializer):
students = serializers.SerializerMethodField()
homeworks = serializers.SerializerMethodField()
courses = serializers.SerializerMethodField()
def get_students(self, obj):
students = obj.student_set.all()
serializer = StudentSerializer(students, many=True)
return serializer.data
def get_homeworks(self, obj):
homeworks = obj.homework_set.all()
serializer = HomeworkSerializer(homeworks, many=True, read_only=True)
return serializer.data
def get_courses(self, obj):
courses = obj.courses_set.all()
serializer = CourseSerializer(courses, many=True, read_only=True)
return serializer.data
class Meta:
model = Student
fields = ('student_id','firstName','age','homeworks','courses')
views.py
class CreatedData(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = Combined_Serializer
def create(self, request, pk=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
student_id = serializer.data['student_id']
name = serializer.data['Name']
student_age = serializer.data['age']
print("SERIALIZER.DATA >" , serializer.data)
print("HEADERS", headers)
I'm not able to see the Course
& Homework
models fields when I print serializer.data
, only fields from Student
models I can get the fields.
I want to design a view which I can access the all fields from all tables. I want to get them after user click submit
button.
How to design the view to get that other information in Django rest API?
thanks for your help in advance!
CodePudding user response:
Combined_Serializer
returns only the student fields because:
- model setting: in
Meta
,model = Student
- relation:
students = obj.student_set.all()
<- can you explain how this relation works?
If you have to specify model in the serializer, you can create an abstract model that has foreign key relation to all three models - Student, Course, Homework and modify your serializer accordingly. But it's recommended only if the class will have a solid use and a single responsibility.
class StudentCourseHomework:
```you can name it better with abstract term that explains
what you want to do with this model```
student = models.ForeignKey()
course = models.ForeignKey()
homework = models.ForeignKey()
class Meta:
abstract = True
Alternatives
You can create serializer without model using serializers.Serializer
.
class CombinedSerializer(serializers.Serializer):
class Meta:
fields = StudentSerializer.Meta.fields
CoursetSerializer.Meta.fields
HomeworkSerializer.Meta.fields
Other things
- removing underscore in class name will be more clean.
- if you really intend to make serializer that has all three models' fields, it might be better to use a more specific name.
- Or you can use the view class name like
CreateDataSerializer
CodePudding user response:
class CombinedCreateSerializer(serializers.Serializer):
students = NewStudentSerializer()
homeworks = NewHomeworkSerializer() # with minimal fields required to create like student_id is not required as it will be obtained later
courses = NewCourseSerializer() # with minimal fields
def create(self, validated_data):
student = Student.objects.create(**validated_data['students'])
# add student id in courses data
courses = Course.objects.create(**validated_data['courses'])
# add student id and course id in homeworks data
homeworks = Homework.objects.create(**validated_data['homework'])
return student
In views
class CreatedData(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = Combined_Serializer # your previously defined
def create(self, request):
# first verify combined create serializer
combined_create_serializer = CombinedCreateSerializer(data=request.data)
combined_create_serializer.is_valid(raise_exception=True)
student_instance = self.perform_create(combined_create_serializer)
combined_serializer_data = self.get_serializer(student_instance) # your final data here which is from previously defined
# ... rest of your codes
In the past I have achieved doing similar like this; good luck