As stated in this question
With Django REST Framework, a standard ModelSerializer will allow ForeignKey model relationships to be assigned or changed by POSTing an ID as an Integer.
I am attempting to update a reverse relationship of the following format:
class Lesson(models.Model):
name = models.TextField()
class Quiz(models.Model):
lesson = models.ForeignKey(Lesson, related_name='quizzes', null=True)
class LessonSerializer(serializers.ModelSerializer):
quizzes = serializers.PrimaryKeyRelatedField(queryset=Quiz.objects.all(), many=True, write_only=True)
class Meta:
model = Lesson
fields = ('quizzes')
When posting an update containing an array of quiz primary keys using LessonSerializer I get TypeError: 'Quiz' instance expected, got '1'.
Is it possible to assign or change a reverse relationship by POSTing an array of primary keys?
You don't need special field in a serializer, DRF serializers is smart enough to figure out related field from a fields value.
class LessonSerializer(serializers.ModelSerializer):
class Meta:
model = Lesson
fields = ('quizzes', 'name')
And you should pass list of ids, if there is only one value it should be a list anyway.
To solve this you need to create a Quiz instance first before you assign it to Lesson. Below's a small change to your code.
class Lesson(models.Model):
name = models.TextField()
class Quiz(models.Model):
lesson = models.ForeignKey(Lesson, related_name='quizzes', null=True)
class LessonSerializer(serializers.ModelSerializer):
quizzes = serializers.PrimaryKeyRelatedField(queryset=Quiz.objects.all(), many=True, write_only=True)
class Meta:
model = Lesson
fields = ('quizzes')
class QuizSerializer(serializers.ModelSerializer):
class Meta:
model = Quiz
fields = ('name')
Create POST request with with quiz then run your post again
Related
I am trying to access child model value from parent model serializer.
Parent Models
class Course(models.Model):
name = models.CharField(max_length=100)
Child Models
class CourseStaff(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE,
related_name='course_staff_course')
staff = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='course_staff_user')
enable = models.BooleanField()
Serializer Class
class TeacherCourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = '__all__'
Expected Result
[
{
id: 1,
name: "",
staff_id: 5, #<---- It will come from the child model.
is_enabled: true #<---- It will come from the child model.
}
]
I tried with this
Serializer
class TeacherCourseSerializer(serializers.ModelSerializer):
enable = serializers.CharField(source='course_staff_course.enable')
class Meta:
model = Course
fields = '__all__'
But getting error
Got AttributeError when attempting to get a value for field `enable` on serializer `TeacherCourseSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Course` instance.
Original exception text was: 'RelatedManager' object has no attribute 'enable'.
How can I achieve this?
Thanks
Course to course staff is one to many relationship. The related manager will not return a single object instead multiple objects. Instead you can try different approach if course is going to have only one staff use OneToOneField and continue in same way.
Other way is to create a nested serializer for staff and the use in the course serializer.
class CourseStaffSerializer(serializers.ModelSerializer):
class Meta:
model = CourseStaff
fields = '__all__'
class TeacherCourseSerializer(serializers.ModelSerializer):
teachers = CourseStaffSerializer(many=True, read_only=True)
class Meta:
model = Course
fields = '__all__'
I know that this question has been asked before, but I've tried so many different solutions and just cannot seem to get it to work. All I am attempting to do is display one object within another on an api call. In this simple example, lets say that I have an artist object and a track object. When I serialize the track object, I would like for the artist object that it refers to using a foreign key to be displayed.
Here is my models.py:
from django.db import models
class Artist(models.Model):
artist_name = models.CharField(max_length=100)
class Track(models.Model):
artist = models.ForeignKey(Artist, on_delete=models.CASCADE, verbose_name='Artist' )
track_name = models.CharField(max_length=100)
and my serializers.py
from rest_framework import serializers
from . import models
class ArtistSerializer(serializers.ModelSerializer):
class Meta:
model = models.Artist
fields = '__all__'
class TrackSerializer(serializers.ModelSerializer):
artist = ArtistSerializer(read_only=True)
class Meta:
model = models.Track
fields = ('id', 'track_name', 'artist')
If anyone could help, it would be greatly appreciated!
Thanks
I have been getting my head around these basics but I am not getting it right. I am trying to associate my view to my user model using team which is a foreign key. When I try to create of a gps, I get an error saying "team is a required field" but instead it should be read only. The team attribute should be filled automatically with the id of the currentUser
Model
class User(models.Model):
first_name = models.CharField(max_length=200,blank=False)
last_name = models.CharField(max_length=200, blank=False)
class Gps(models.Model):
location = models.CharField(max_length=200,blank=False)
team= models.ForeignKey(User, on_delete=models.CASCADE)
serializers
class GpsSerializer(serializers.ModelSerializer):
class Meta:
model = Gps
fields = ('id','location','team')
view
class Gps_list(generics.ListCreateAPIView):
queryset = Gps.objects.all()
serializer_class = GpsSerializer
team = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
There are two changes needed. First, team field definition should be moved to serializer class instead of view. Second, you should use Django's contrib.auth.User model instead of your definition of User, as because serializers.CurrentUserDefault() will bring request.user only. So you should remove your User definition and import that to your models.py:
from django.contrib.auth.models import User
Further steps would be to replace read_only=True with queryset=User.objects.all() to allow create.
models.py:
class Station(models.Model):
station = models.CharField()
class Flat(models.Model):
station = models.ForeignKey(Station, related_name="metro")
# another fields
Then in serializers.py:
class StationSerializer(serializers.ModelSerializer):
station = serializers.RelatedField(read_only=True)
class Meta:
model = Station
class FlatSerializer(serializers.ModelSerializer):
station_name = serializers.RelatedField(source='station', read_only=True)
class Meta:
model = Flat
fields = ('station_name',)
And I have an error:
NotImplementedError: RelatedField.to_representation() must be implemented.
If you are upgrading from REST framework version 2 you might want ReadOnlyField.
I read this, but it does not help me.
How to fix that?
Thanks!
RelatedField is the base class for all fields which work on relations. Usually you should not use it unless you are subclassing it for a custom field.
In your case, you don't even need a related field at all. You are only looking for a read-only single foreign key representation, so you can just use a CharField.
class StationSerializer(serializers.ModelSerializer):
station = serializers.CharField(read_only=True)
class Meta:
model = Station
class FlatSerializer(serializers.ModelSerializer):
station_name = serializers.CharField(source='station.name', read_only=True)
class Meta:
model = Flat
fields = ('station_name', )
You also appear to want the name of the Station object in your FlatSerializer. You should have the source point to the exact field, so I updated it to station.name for you.
I have a model form that I use to update a model.
class Turtle(models.Model):
name = models.CharField(max_length=50, blank=False)
description = models.TextField(blank=True)
class TurtleForm(forms.ModelForm):
class Meta:
model = Turtle
Sometimes I don't need to update the entire model, but only want to update one of the fields. So when I POST the form only has information for the description. When I do that the model never saves because it thinks that the name is being blanked out while my intent is that the name not change and just be used from the model.
turtle_form = TurtleForm(request.POST, instance=object)
if turtle_form.is_valid():
turtle_form.save()
Is there any way to make this happen? Thanks!
Only use specified fields:
class FirstModelForm(forms.ModelForm):
class Meta:
model = TheModel
fields = ('title',)
def clean_title(self....
See http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#controlling-which-fields-are-used-with-fields-and-exclude
It is common to use different ModelForms for a model in different views, when you need different features. So creating another form for the model that uses the same behaviour (say clean_<fieldname> methods etc.) use:
class SecondModelForm(FirstModelForm):
class Meta:
model = TheModel
fields = ('title', 'description')
If you don't want to update a field, remove it from the form via the Meta exclude tuple:
class Meta:
exclude = ('title',)