I'm trying to overwrite the value of the DateField field if it's empty. As usual, a field with this type is not validated during serialization if the value is not an object of datetime class. I need to write null to the database if the value is empty. To do this, I'm trying to change the clean() model method.
serializer.py
class VendorsSerializer(serializers.ModelSerializer):
contacts = VendorContactSerializer(many=True)
class Meta:
model = Vendors
fields = (...
'nda',
'contacts',)
def create(self, validated_data):
contact_data = validated_data.pop('contacts')
vendor = Vendors.objects.create(**validated_data)
vendor.full_clean()
for data in contact_data:
VendorContacts.objects.create(vendor=vendor, **data)
return vendor
models.py
class Vendors(models.Model):
...
nda = models.DateField(blank=True, null=True)
def clean(self):
if self.nda == "":
self.nda = None
view.py
class VendorsCreateView(APIView):
"""Create new vendor instances from form"""
permission_classes = (permissions.AllowAny,)
serializer_class = VendorsSerializer
def post(self, request, *args, **kwargs):
serializer = VendorsSerializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
except ValidationError:
return Response({"errors": (serializer.errors,)},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(request.data, status=status.HTTP_200_OK)
Why clean() doesnt run ?
json
{
"vendor_name": "The Greey swAlsbudam2",
"country": "Belgium",
"nda": "",
"contacts": [{"contact_name": "Mrk", "phone": "2373823", "email": "ryryr#gmail.com"},
{
"contact_name": "Uio",
"phone": "34567",
"email": "ryfhhryr#gmail.com"
}
]
}
Because you are creating instance before calling full_clean here
vendor = Vendors.objects.create(**validated_data)
vendor.full_clean()
The first line creates the object in the database (with an empty string). The second line performs cleaning but does not save again in the database. You have to perform cleaning before saving.
vendor = Vendors(**validated_data)
vendor.full_clean()
vendor.save()
Related
I 'm trying to build a view to create a new user.
In this view I call a serializer which must use 2 different tables
Here is the request :
{
"email": "email#example.com",
"name": "name",
"surname": "surname",
"phone": "0601234567",
"password": "azerty",
"address": {
"osm": "N65719518",
"lat": 18.072,
"lon": 36.087,
"street": "Name of the street",
"postalCode": 123456,
"city": "name of the city"
}
}
I would like to separate the "address" field from my query which corresponds to another table.
For this I use the pop method in my serializer
Here is my serializer :
serializers.py
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = '__all__'
class RegistrationSerializer(serializers.ModelSerializer):
"""Create an account"""
address = AddressSerializer()
class Meta:
model = User
exclude = ['joined', 'lastLogin', 'is_staff', 'is_superuser', 'is_admin', 'is_active']
extra_kwargs = {
# For security to hide the password (we can't read it)
'password': {'write_only': True},
}
def save(self):
# Pop the address of the user
address = self.validated_data.pop("address")
user = User.objects.create(**self.validated_data)
user.set_password(self.validated_data['password'])
user.save()
Address.objects.create(user=user, **address)
token = Token.objects.get(user=user)
confirmation(user, token.key)
return user
Here is the view in which I call my serializer :
views.py
class HandleUsers(HandleUsersView):
def post(self, request):
"""For everyone"""
serializer = RegistrationSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
token = Token.objects.get(user=user).key
return Response({**{'id': user.id},**serializer.data, **{'token': token}})
return Response(serializer.errors)
Here is the full error that I get :
"Got KeyError when attempting to get a value for field address on serializer RegistrationSerializer.\nThe serializer field might be named incorrectly and not match any attribute or key on the OrderedDict instance.\nOriginal exception text was: 'address'."
Thank you by advance for your help
For a better explanation to my comment.
save() calls either create() or update(). Indeed you can override save() but I donĀ“t recommend it.
If you do
serializer = RegistrationSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
your save() calls create() because a value was assigned to data.
If you do
instance = User.objects.get(....)
serializer = RegistrationSerializer(data=request.data, instance=instance)
if serializer.is_valid():
user = serializer.save()
your save() calls update() because values were assigned to data and instance.
If you want you can pass additional attributes to save()
drf saving instance
This is a problem which I've been fighting with for a while and now I gave up. I am writing a User/Profile model in Django 2.0.2 (python 3.6 + postgres 10 on Linux) which is as follows :
class Config_Table(models.Model):
entity_name = models.TextField(primary_key=True)
category = models.TextField()
description = models.TextField(blank=True,default='')
Above table keeps some static information which gets me into trouble.
class UserProfile(AbstractUser):
"The Profile of a user with details are stored in this model."
username = models.TextField(primary_key=True, max_length=11)
first_name = models.TextField(max_length=50,blank=True,default='')
last_name = models.TextField(max_length=100,blank=True,default='')
phone_number = models.TextField(max_length=11,blank=True,default='')
avatar = models.ImageField(blank=True, upload_to='Pictures/')
GENDER_CHOICES = (
('M','Male'),
('F','Female'),
)
gender = models.CharField(max_length=1,choices=GENDER_CHOICES, default='M')
city = models.TextField(max_length=25, blank=True, default='NY')
description = models.TextField(max_length=2000, blank=True, default='')
interests = models.ManyToManyField(Config_Table, blank=True, default='')
date_of_birth = models.DateField(blank=True)
official_docs = models.ImageField(blank=True, upload_to='Pictures/')
team_name = models.TextField(blank=True,default='')
debit_card_number = models.IntegerField(blank=True, default=0)
MUSIC_CHOICES = (
('Rock','Rock Music'),
('Trad','Traditional Music'),
('Elec','Electronic Music'),
('Clas','Classical Music')
)
favorite_music = ArrayField(models.TextField(blank=True,default=''),size=2,blank=True, default='{}')
class Meta:
permissions=(("User","User level permission"),
("Tour","Tourleader level permission"),
("Admin","Administrators"))
my views.py :
class UserList(APIView):
"""
List all users, or create a new user.
"""
def get(self, request, format=None):
users = UserProfile.objects.all()
target_users = []
for user in users.iterator():
if user.is_superuser == False:
target_users.append(user)
serializer = UserProfileSerializer(target_users, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = UserProfileSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
print(serializer.errors)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class UserDetail(APIView):
"""
Retrieve, update or delete a User.
"""
pk_url_kwarg = 'username'
def get_object(self, pk):
try:
return UserProfile.objects.get(pk=pk)
except UserProfile.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
user = self.get_object(pk=pk)
if user.is_superuser == False:
serializer = UserProfileSerializer(user)
return Response(serializer.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
def put(self, request, pk, format=None):
user = self.get_object(pk)
serializer = UserProfileSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
for attr, value in serializer.validated_data.items():
if attr == 'password' and attr is None:
serializer.validated_data['password'] = user.password
break
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
user= self.get_object(pk)
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
and my serializers.py:
def create(self, validated_data):
hashed_password = make_password(validated_data['password']) # get the hashed password
print(validated_data)
user = UserProfile(
username=validated_data['username'],
email = validated_data['email'],
first_name= validated_data['first_name'],
last_name= validated_data['last_name'],
phone_number=validated_data['phone_number'],
avatar=validated_data.pop('avatar'),
gender=validated_data['gender'],
city=validated_data['city'],
description=validated_data['description'],
date_of_birth=validated_data.pop('date_of_birth'),
# user_type=validated_data['user_type'],
official_docs=validated_data.pop('official_docs'),
team_name=validated_data['team_name'],
debit_card_number=validated_data['debit_card_number'],
favorite_music=validated_data['favorite_music'],
)
user.set_password(hashed_password)
interest = validated_data['interests']
user.interests.add(validated_data['interests'])
user.groups.add(validated_data['groups'])
user.save()
return user
The problem I have happens inside the serializer, when I want to submit some JSON object through http, like below (sample data for testing) :
{
"username": "12345678004",
"password": "thisisatest",
"last_login": null,
"is_superuser": false,
"email" : "sample#gmail.com",
"first_name": "AAA",
"last_name": "BBB",
"phone_number": "12045678000",
"gender": "M",
"city": "NY",
"description": "",
"date_of_birth": "2010-03-28",
"team_name": "",
"avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxAPBhAREQ8QDQ8PDxIWEBAWFRANDxAQFRUWFhURExUYHSggGBolGxUTITEhJSkrLi4uFx8zODMsNygtLisBCgoKBQUFDgUFDisZExkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrK//AABEIAOEA4QMBIgACEQEDEQH/xAAbAAEAAgMBAQAAAAAAAAAAAAAABAUBAgMGB//EADIQAQABAwEHAQYEBwAAAAAAAAABAgMRBAUSITFBUXFhIjKRobHBM4HR8RMjQmJyguH/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/APpgCIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDIAAAAAAAAAAAAAAAAAAAAAAANrduaq8RGZkGqVY0FdXON2PXn8E/SaOKIzPGrv0jwlAhW9nURHHNU/B3p0tER7sOwDjVpaJj3YcbmzqJjhmJTAFPf0FdPGPaj5/BEejRtVo4rjPKrv38gpRtdtzTXiYxLUAAAAAAAAAAAAAAAAAACIzOO670emi3b/unnP2Q9l2M1zVPKOXlaAAAAAAAAA4azTxct+scpUlVMxVieEw9ErNq2MVRVHXn57grwAAAAAAAAAAAAAAAAbW4zciO8x9QXekt7unpj0diOQAAAAAAAAA5aq3vWJj04eXUB5yRtejF2fMtQAAAAAAAAAAAAAAG9n8an/KPq0InE57A9GMUTmiJ7wyAAAAAAAAADFdWKJntAKDUT/Oq8tGa5zXPlgAAAAAAAAAAAAAAAAFxs29vWMdafolqLR39y9E9J4T4XkTmMxxiQZAAAAAAAARNpXd2xjrV9OqVM4jM8IhSay/v3pnpHLwDgAAAAAAAAAAAAAAAAAAnaDWbvs1cuk9kEB6OJ4Cp0F65E4iJqp7f9W0cgAAAAAVevuXJ4TE00+nHPkDX6ze9mn3es90AAAAAAAAAAAAAAAAAAAddPYqrrxH5z0gHOiiaqsRGZWWm2dEca+Pp0S9Pp6aKeEces9ZdQYpiIjEcIZAAAAAAnkAIep0FNWZp9mfkq71mqirEx+kvQNLtqKqcTGQefEjV6Sbc96Z5T9kcAAAAAAAAAAAAAGaKZmqIjnIN9PZmu5iPznsu7FmKLeI/dppNPFu1jrPOXcAAAAAAAAAAAAGK6YmnExmJUut0v8OvvTPKfsu2l63FduYnlIPPje/ami5MT+8NAAAAAAAAAAAFlsvT/wBc/wCv6oFm3vXYiOsr6imKaIiOUA2AAAAAAAAAAAAAAABE2jp9+1mPep+cKd6NSa6zuX57TxgEcAAAAAAAAAFjsm1xmr8o+6ycdJb3dPTHpxdgAAAAAAAAAAAAAAAAEPadresZ60/RMa105omO8A88NrlOLkx2lqAAAAAAA6aejev0x3lzStmU51XiJkFyAAAAAAAAAAAAAAAAAAACl2jRjUz6oyw2vT7VM+ivAAAAAAATdk/jz4AFsAAAAAAAAAAAAAAAAAAACBtb8KPKrAAAAAH/2Q==",
"official_docs": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxAPBhAREQ8QDQ8PDxIWEBAWFRANDxAQFRUWFhURExUYHSggGBolGxUTITEhJSkrLi4uFx8zODMsNygtLisBCgoKBQUFDgUFDisZExkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrK//AABEIAOEA4QMBIgACEQEDEQH/xAAbAAEAAgMBAQAAAAAAAAAAAAAABAUBAgMGB//EADIQAQABAwEHAQYEBwAAAAAAAAABAgMRBAUSITFBUXFhIjKRobHBM4HR8RMjQmJyguH/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/APpgCIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDIAAAAAAAAAAAAAAAAAAAAAAANrduaq8RGZkGqVY0FdXON2PXn8E/SaOKIzPGrv0jwlAhW9nURHHNU/B3p0tER7sOwDjVpaJj3YcbmzqJjhmJTAFPf0FdPGPaj5/BEejRtVo4rjPKrv38gpRtdtzTXiYxLUAAAAAAAAAAAAAAAAAACIzOO670emi3b/unnP2Q9l2M1zVPKOXlaAAAAAAAAA4azTxct+scpUlVMxVieEw9ErNq2MVRVHXn57grwAAAAAAAAAAAAAAAAbW4zciO8x9QXekt7unpj0diOQAAAAAAAAA5aq3vWJj04eXUB5yRtejF2fMtQAAAAAAAAAAAAAAG9n8an/KPq0InE57A9GMUTmiJ7wyAAAAAAAAADFdWKJntAKDUT/Oq8tGa5zXPlgAAAAAAAAAAAAAAAAFxs29vWMdafolqLR39y9E9J4T4XkTmMxxiQZAAAAAAAARNpXd2xjrV9OqVM4jM8IhSay/v3pnpHLwDgAAAAAAAAAAAAAAAAAAnaDWbvs1cuk9kEB6OJ4Cp0F65E4iJqp7f9W0cgAAAAAVevuXJ4TE00+nHPkDX6ze9mn3es90AAAAAAAAAAAAAAAAAAAddPYqrrxH5z0gHOiiaqsRGZWWm2dEca+Pp0S9Pp6aKeEces9ZdQYpiIjEcIZAAAAAAnkAIep0FNWZp9mfkq71mqirEx+kvQNLtqKqcTGQefEjV6Sbc96Z5T9kcAAAAAAAAAAAAAGaKZmqIjnIN9PZmu5iPznsu7FmKLeI/dppNPFu1jrPOXcAAAAAAAAAAAAGK6YmnExmJUut0v8OvvTPKfsu2l63FduYnlIPPje/ami5MT+8NAAAAAAAAAAAFlsvT/wBc/wCv6oFm3vXYiOsr6imKaIiOUA2AAAAAAAAAAAAAAABE2jp9+1mPep+cKd6NSa6zuX57TxgEcAAAAAAAAAFjsm1xmr8o+6ycdJb3dPTHpxdgAAAAAAAAAAAAAAAAEPadresZ60/RMa105omO8A88NrlOLkx2lqAAAAAAA6aejev0x3lzStmU51XiJkFyAAAAAAAAAAAAAAAAAAACl2jRjUz6oyw2vT7VM+ivAAAAAAATdk/jz4AFsAAAAAAAAAAAAAAAAAAACBtb8KPKrAAAAAH/2Q==",
"debit_card_number": 0,
"favorite_music": [],
"groups": [1],
"user_permissions": [],
"interests": ["Ski"]
}
It always returns TypeError:
user.interests.add(validated_data['interests'])
Exception Type: TypeError at /users/
Exception Value: unhashable type: 'list'
I have tried different ways of implementing "interests" and "groups" in the serializer create function. I tried parsing validated data, selecting the child from Config_table and adding it here, but none of them work.
I actually have the same problem with "groups" field as well. It seems that Django cannot unhash the validated data in serializer and there it throws error.
The interesting point is, if I don't fill in the "group" and "interests" field when calling the POST method, it works fine, and later, I can update those fields by calling PUT with no problem.
What can I do with this?
You should pass to interests.add method different interests objects. Not list of names. So first you need get objects by name and then pass unpacking list of objects to add method using * syntax:
intersts = []
for name in validated_data['interests']:
obj, created = Config_Table.objects.get_or_create(entity_name=name)
interests.append(obj)
user.interests.add(*interests)
OK, I resolved the issue like below :
I changed Config_table model and added default django id field (technically, just removed primary_key from entity_name row, so django added the id AutoField by default). Re-populated the database and changed the code in serializers.py like below..using the answer of #neverwalkaloner :
user = UserProfile(
...
)
user.set_password(hashed_password)
user.save()
interests = []
for id in validated_data['interests']:
interests.append(id)
user.interests.add(*interests)
for name in validated_data['groups']:
user.groups.add(name)
user.save()
return user
Note that I had to save the entity before I wanted to work with a ManyToManyField. As django expects to see something in the database prior to adding some entities for ManyToMany fields. Keep that note in your code.
With this workaround, my problem has been resolved. But it is still strange to me why I could not use a primary key rather than the default. If anyone has an answer to this question, I'm more than willing to hear.
Using django-rest framework 3.3.3
I am trying to update existing data in my database using the django-rest framework, following a previously posted solution :django-rest-framework 3.0 create or update in nested serializer
I'm fairly new to django in general, and the rest framework.
Given the following models:
class Clinic(models.Model):
name = models.TextField()
streetNumber = models.IntegerField()
unitNumber = models.CharField(max_length=10, blank=True, null=True)
streetName = models.CharField(max_length=50)
city = models.CharField(max_length=50)
province = models.CharField(max_length=3)
country = models.CharField(max_length=30)
postalCode = models.CharField(max_length=6)
phone = models.CharField(max_length=10)
def __str__(self):
return self.name
class Time (models.Model):
clinic = models.ForeignKey(Clinic, related_name='times')
appointmentTime = models.CharField(max_length=100)
def __str__(self):
return self.appointmentTime
And the following serializers:
class TimeSerializer(serializers.ModelSerializer):
class Meta:
model = Time
fields = ('appointmentTime',)
class ClinicSerializer(serializers.ModelSerializer):
times = TimeSerializer(many=True)
class Meta:
model = Clinic
fields = '__all__'
def update(self, instance, validated_data):
instance.name = validated_data['name']
instance.streetName = validated_data['streetName']
instance.unitNumber = validated_data['unitNumber']
instance.city = validated_data['city']
instance.province = validated_data['province']
instance.country = validated_data['country']
instance.postalCode = validated_data['postalCode']
instance.phone = validated_data['phone']
#print(instance)
instance.save()
times_data = [item['id'] for item in validated_data['times']]
print(times_data)
for time in instance.clinics:
if time.id not in times_data:
time.delete()
for item in validated_data['times']:
time = Time(id=item['id'], clinic=instance,
appointmentTime=item['appointmentTime'])
time.save()
return instance
And the following view:
class ClinicList(APIView):
def get(self,request):
clinics = Clinic.objects.all()
serializer = ClinicSerializer(clinics, many=True)
return Response({'results': serializer.data})
def post(self, request):
serializer = ClinicSerializer(data=request.data)
if serializer.is_valid():
serializer.update(Clinic, serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
And the following existing data:
{
"id": 2,
"times": [
{
"appointmentTime": "time3"
},
{
"appointmentTime": "time3"
}
],
"name": "Example Clinic",
"streetNumber": 123,
"unitNumber": "",
"streetName": "Example Street",
"city": "Exampletown",
"province": "Ont",
"country": "Canada",
"postalCode": "A1A1A1",
"phone": "9059059059"
}
With my JSON POST data being:
{
"id": 2,
"times": [
{
"appointmentTime": "AAAA"
},
{
"appointmentTime": "time3"
}
],
"name": "Example Clinic",
"streetNumber": 123,
"unitNumber": "",
"streetName": "Example Street",
"city": "Exampletown",
"province": "Ont",
"country": "Canada",
"postalCode": "A1A1A1",
"phone": "9059059059"
}
My problem is that I am receiving an error stating:
File"C:\Users\colin\Documents\GitHub\Walk_inExpressAPI\clinics\serializer.py",
line 31, in update
instance.save()
TypeError: save() missing 1 required positional argument: 'self'
I'm not sure what Im doing wrong here, or what value to supply to instance.save() to correct this error. I've checked that instance was an object of type Clinic.
What i'm trying to accomplish is that each Clinic can have multiple appointment times. They wont share appointmentTime objects, because I would like to be able to delete an appointentTime each time a new appointment is booked, and each clinic will require its own set of available appointmentTimes.
Hopefully you guys can help lead me in the right direction! Thanks!
You should user the method PUT in your APIView:
def put(self, request, pk):
clinics = Clinic.objects.get(pk=pk)
serializer = ClinicSerializer(clinics, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
so in you url.py should have a pk, to identify the object you want to modify:
urlpatterns = [
url(r'^snippets/$', views.ClinicList.as_view()),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.ClinicList.as_view()),
]
In update method change the instance attributes like:
instance.name = validated_data.get('name',instance.name)
Try to see the example in DRF page as well.
The problem: when I pass someModelSerializer.data as a nested data for anotherModelSerializer, and after that I pass anotherModelSerializer.data to Response, in the response I see only two fields of SomeModel instead of 5. But when I pass someModelSerializer.data directly to Response, I can see that all model fields are present.
Details below.
I have TranslationHistory model
class TranslationHistory(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
string = models.CharField(max_length=255)
count = models.IntegerField(default=1)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='translation_history')
and it's TranslationHistorySerializer
class TranslationHistorySerializer(serializers.ModelSerializer):
class Meta:
model = TranslationHistory
user = serializers.PrimaryKeyRelatedField(read_only=True, default=None)
count = serializers.IntegerField(read_only=True)
def validate_user(self, value):
return self.context['request'].user
def update(self, instance, validated_data):
instance.count += 1
instance.save()
return instance
I also have a virtual entity Translation, which is not a model. It has it's own serializer.
class TranslationSerializer(serializers.Serializer):
translation_history = TranslationHistorySerializer() # nested serializer
translation = serializers.CharField()
In my view, if I do like this,
history = TranslationHistory().findByUserAndString(request.user, string)
historySerializer = TranslationHistorySerializer(history)
return Response(historySerializer.data, status=status.HTTP_200_OK)
I have the response like this.
{
"id": 18,
"user": 1,
"count": 72,
"created": "2015-07-15T15:35:50.751219Z",
"updated": "2015-07-24T15:37:04.247469Z",
"string": "hello"
}
But if in my view I do like this,
history = TranslationHistory().findByUserAndString(request.user, string)
historySerializer = TranslationHistorySerializer(history)
serializer = TranslationSerializer(
data={
'translation_history': historySerializer.data,
'translation': 'hello'
},
context={'request': request}
)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)
I have the response like this.
{
"translation_history": {
"user": 1,
"string": "hello"
},
"translation": "hello"
}
Why am I getting only user and string fields in this case?
I'm currently working on Django with Django Rest Framwork.
I can't update my object within nested object field.
serializer.py
class OwnerSerializer(serializers.ModelSerializer):
class Meta:
model = Owner
fields = ('id', 'name')
class CarSerializer(serializers.ModelSerializer):
owner = ownerSerializer(many=False, read_only=False)
class Meta:
model = Car
fields = ('id', 'name', 'owner')
view.py
class OwnerViewSet(viewsets.ModelViewSet):
queryset = Owner.objects.all()
serializer_class = OwnerSerializer
class CarViewSet(viewsets.ModelViewSet):
serializer_class = CarSerializer
queryset = Car.objects.all()
def create(self, request):
serialized = self.serializer_class(data=request.DATA)
if serialized.is_valid():
serialized.save()
return Response(status=HTTP_202_ACCEPTED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
When I do this :
Request URL:http://localhost:9000/api/v1/cars/1/?format=json
Request Method:PUT
Request Paylod :
{
"id":1,
"name": "TEST",
"ower": {
"id":1,
"name": "owner_test"
}
}
I get the following Response :
The `.update()` method does not support writable nestedfields by default.
Write an explicit `.update()` method for serializer `app.serializers.CarSerializer`,
or set `read_only=True` on nested serializer fields.
Knowing :
I want to keep the owner serialization on GET;
We can imagine the car nested by another object and ect...
How can I do if i want to change the owner when I update the car.
A little late, but, Try this,
class OwnerSerializer(serializers.ModelSerializer):
class Meta:
model = Owner
fields = ('id', 'name')
extra_kwargs = {
'id': {
'read_only': False,
'required': True
}
} #very important
def create(self, validated_data):
# As before.
...
def update(self, instance, validated_data):
# Update the instance
instance.some_field = validated_data['some_field']
instance.save()
# Delete any detail not included in the request
owner_ids = [item['owner_id'] for item in validated_data['owners']]
for owner in cars.owners.all():
if owner.id not in owner_ids:
owner.delete()
# Create or update owner
for owner in validated_data['owners']:
ownerObj = Owner.objects.get(pk=item['id'])
if ownerObje:
ownerObj.some_field=item['some_field']
....fields...
else:
ownerObj = Owner.create(car=instance,**owner)
ownerObj.save()
return instance
Just in-case someone stumbles on this
had the same error in my case but setting read_only to True fixed it for me.
owner = ownerSerializer(many=False, read_only=True)
Note that, this field won't appear in the form when posting data to the api.