|
| 1 | +# --- Test models and serializers for SerializerMethodField in unique_together --- |
| 2 | +class UniqueTestModel(models.Model): |
| 3 | + name = models.CharField(max_length=100) |
| 4 | + description = models.CharField(max_length=100, null=True, blank=True) |
| 5 | + other_field = models.CharField(max_length=100, default="default_value") |
| 6 | + |
| 7 | + class Meta: |
| 8 | + unique_together = [("name", "description")] |
| 9 | + |
| 10 | + def __str__(self): |
| 11 | + return f"{self.name} - {self.description or 'No description'}" |
| 12 | + |
| 13 | + |
| 14 | +class UniqueTestModelSerializer(serializers.ModelSerializer): |
| 15 | + description = serializers.SerializerMethodField() |
| 16 | + |
| 17 | + class Meta: |
| 18 | + model = UniqueTestModel |
| 19 | + fields = ["name", "description", "other_field"] |
| 20 | + |
| 21 | + def get_description(self, obj): |
| 22 | + if obj.description: |
| 23 | + return f"Serialized: {obj.description}" |
| 24 | + return "Serialized: No description provided" |
| 25 | + |
| 26 | + |
| 27 | +# --- Test case for SerializerMethodField in unique_together --- |
| 28 | +class TestSerializerMethodFieldInUniqueTogether(TestCase): |
| 29 | + def test_serializer_method_field_not_hidden_in_unique_together(self): |
| 30 | + # 1. Instantiate the serializer |
| 31 | + serializer = UniqueTestModelSerializer() |
| 32 | + |
| 33 | + # 2. Assert that the 'description' field is not a HiddenField |
| 34 | + self.assertFalse( |
| 35 | + isinstance(serializer.fields['description'], serializers.HiddenField), |
| 36 | + "Field 'description' should not be a HiddenField." |
| 37 | + ) |
| 38 | + |
| 39 | + # 3. Assert that 'description' is a SerializerMethodField |
| 40 | + self.assertTrue( |
| 41 | + isinstance(serializer.fields['description'], serializers.SerializerMethodField), |
| 42 | + "Field 'description' should be a SerializerMethodField." |
| 43 | + ) |
| 44 | + |
| 45 | + # 4. Assert that the 'description' field is present in the serializer's output |
| 46 | + instance = UniqueTestModel.objects.create(name="TestName", description="TestDesc") |
| 47 | + serializer_output = UniqueTestModelSerializer(instance).data |
| 48 | + self.assertIn("description", serializer_output) |
| 49 | + self.assertEqual(serializer_output["description"], "Serialized: TestDesc") |
| 50 | + |
| 51 | + instance_no_desc = UniqueTestModel.objects.create(name="TestNameNoDesc") |
| 52 | + serializer_output_no_desc = UniqueTestModelSerializer(instance_no_desc).data |
| 53 | + self.assertEqual(serializer_output_no_desc["description"], "Serialized: No description provided") |
| 54 | + |
| 55 | + # 5. Perform a validation test |
| 56 | + # Create an initial instance |
| 57 | + UniqueTestModel.objects.create(name="UniqueName", description="UniqueDesc") |
| 58 | + |
| 59 | + # Attempt to validate data that would violate unique_together |
| 60 | + invalid_data = {"name": "UniqueName", "description": "UniqueDesc", "other_field": "some_value"} |
| 61 | + serializer_invalid = UniqueTestModelSerializer(data=invalid_data) |
| 62 | + with self.assertRaises(serializers.ValidationError) as context: |
| 63 | + serializer_invalid.is_valid(raise_exception=True) |
| 64 | + self.assertIn("non_field_errors", context.exception.detail) # Check for unique_together error |
| 65 | + self.assertTrue(any("unique test model with this name and description already exists" in str(err) |
| 66 | + for err_list in context.exception.detail.values() for err in err_list)) |
| 67 | + |
| 68 | + |
| 69 | + # Attempt to validate data that would violate unique_together (with null description) |
| 70 | + UniqueTestModel.objects.create(name="UniqueNameNull", description=None) |
| 71 | + invalid_data_null = {"name": "UniqueNameNull", "description": None, "other_field": "some_value"} |
| 72 | + serializer_invalid_null = UniqueTestModelSerializer(data=invalid_data_null) |
| 73 | + |
| 74 | + with self.assertRaises(serializers.ValidationError) as context_null: |
| 75 | + serializer_invalid_null.is_valid(raise_exception=True) |
| 76 | + |
| 77 | + self.assertIn("non_field_errors", context_null.exception.detail) |
| 78 | + self.assertTrue(any("unique test model with this name and description already exists" in str(err) |
| 79 | + for err_list in context_null.exception.detail.values() for err in err_list)) |
| 80 | + |
| 81 | + |
| 82 | + # Attempt to validate valid data |
| 83 | + valid_data = {"name": "NewName", "description": "NewDesc", "other_field": "another_value"} |
| 84 | + serializer_valid = UniqueTestModelSerializer(data=valid_data) |
| 85 | + self.assertTrue(serializer_valid.is_valid(raise_exception=True)) |
| 86 | + self.assertEqual(serializer_valid.validated_data['name'], "NewName") |
| 87 | + # Note: 'description' from SerializerMethodField won't be in validated_data for writes by default. |
| 88 | + # The key here is that the serializer construction and unique_together validation works. |
| 89 | + # The model's description field will be None or the provided value during create/update. |
| 90 | + |
| 91 | + # Validate data where description is not provided (should use model field's null=True) |
| 92 | + valid_data_no_desc = {"name": "NameOnly"} # description and other_field will use defaults or be None |
| 93 | + serializer_valid_no_desc = UniqueTestModelSerializer(data=valid_data_no_desc) |
| 94 | + self.assertTrue(serializer_valid_no_desc.is_valid(raise_exception=True)) |
| 95 | + self.assertIsNone(serializer_valid_no_desc.validated_data.get('description')) |
| 96 | + self.assertEqual(serializer_valid_no_desc.validated_data['other_field'], "default_value") # check other_field default |
| 97 | + |
| 98 | + # Check saving the instance |
| 99 | + saved_instance = serializer_valid_no_desc.save() |
| 100 | + self.assertEqual(saved_instance.name, "NameOnly") |
| 101 | + self.assertIsNone(saved_instance.description) |
| 102 | + self.assertEqual(saved_instance.other_field, "default_value") |
| 103 | + |
| 104 | + # Check that SerializerMethodField value is used in representation after save |
| 105 | + output_after_save = UniqueTestModelSerializer(saved_instance).data |
| 106 | + self.assertEqual(output_after_save['description'], "Serialized: No description provided") |
| 107 | +# The following imports are assumed to be present in the original file and are necessary |
| 108 | +# for the extracted code to be contextually correct. |
| 109 | +from django.db import models |
| 110 | +from django.test import TestCase |
| 111 | +from rest_framework import serializers |
0 commit comments