Reputation: 145
Trying to implement and test my own serializer, I have the following issue:
Searched for many question including this error message, I didn't manage to find any solution to my problem.
DRF Foreign key misusage?
ValueError: Cannot assign "'effcad53-bc45-41fa-be43-4f22c0376eb5'": "Product.related_workspace" must be a "Workspace" instance.
The Workspace class:
class Workspace(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
)
related_login = models.ForeignKey(
Login,
on_delete=models.PROTECT,
)
description = models.CharField(
max_length=150,
)
def __str__(self):
login = Login.objects.get(pk=self.related_login_id)
return f'{login.username} ({self.description})'
class Meta:
db_table = 'workspaces'
The Product class:
class Product(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
)
related_workspace = models.ForeignKey(
Workspace,
on_delete=models.PROTECT,
)
code = models.CharField(
max_length=50,
)
description = models.CharField(
max_length=150,
)
class Meta:
db_table = 'products'
unique_together = ('related_workspace', 'code',)
The ProductSerializer class:
class ProductSerializer(serializers.Serializer):
id = serializers.UUIDField(read_only=True)
related_workspace = serializers.UUIDField()
code = serializers.CharField()
description = serializers.CharField()
def create(self, validated_data):
return Product.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.related_workspace = validated_data.get('related_workspace', instance.related_workspace)
instance.code = validated_data.get('code', instance.code)
instance.description = validated_data.get('description', instance.description)
instance.save()
return instance
The script I'm using to test my serializer :
PROJECT_NAME = 'api'
def main():
#
from api.applications.product.models import Product
from api.applications.product.serializers import ProductSerializer
#
# Create a product
#
code = 'P001'
description = f'Product {code}'
#
# This is a valid workspace id!
#
related_workspace = 'effcad53-bc45-41fa-be43-4f22c0376eb5'
#
product = Product(
code=code,
description=description,
related_workspace=related_workspace,
)
product.save()
#
serializer = ProductSerializer(product)
print('serializer.data:', serializer.data)
if __name__ == '__main__':
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '%s.settings' % PROJECT_NAME)
import django
django.setup()
main()
Any advice of what I'm missing ?
Upvotes: 1
Views: 231
Reputation: 145
Searching deeper, my mistake was in the way I was creating my Product instance...
In my test script, I just updated:
product = Product(
code=code,
description=description,
related_workspace=related_workspace,
)
To:
product = Product(
code=code,
description=description,
related_workspace=Workspace.objects.get(pk=related_workspace)
)
Upvotes: 0
Reputation: 492
First thing, when you are testing anything in Django its easiest to write test classes in a test
module and use manage.py to run them. All that setup logic you are doing is already in manage.py.
python manage.py test my_app.tests.my_test_class
In your serializer you are setting a foreign key to related_workspace = serializers.UUIDField()
, instead you want to nest your serializers (docs) so it pulls out the Workspace data, serializes, saves, and returns a Workspace object. You should have two serializers and they should look something like this, I am going to use ModelSerializer.
from rest_framework import serializers
# Workspace Serializer
class WorkspaceSerializer(serializers.ModelSerializer):
class Meta:
model = Workspace
fields = "__all__"
class ProductSerializer(serializers.ModelSerializer):
related_workspace = WorkspaceSerializer()
class Meta:
model = Product
fields = ["id", "related_workspace", "code", "description"]
def create(self, validated_data):
workspace_data = validated_data.pop('related_workspace')
workspace_serializer = WorkspaceSerializer(data=workspace_data)
if workspace_serializer.is_valid(reaise_exception=True):
workspace = workspace_serializer.save()
product = Product.objects.create(related_workspace = workspace, **validated_data)
return product
We want to remove the workspace data, then feed it to its own serializer, if that data is valid, we save the serializer and we get a Workspace object in return. Then we can create our Product object with that returned workspace object.
Upvotes: 1