如何临时将HyperlinkedRelatedField设为read_only?现有方法失效
Hey there! Let's figure out why your attempt to make a HyperlinkedRelatedField read-only isn't working, and fix it up.
The key thing here is that HyperlinkedRelatedField has specific initialization logic that makes simply toggling the read_only property after the serializer is created ineffective—unlike the auto-generated fields for model ForeignKeys, which are a bit more straightforward. Here are a couple of reliable ways to dynamically set it to read-only:
1. Dynamically modify the field in the serializer's __init__ method
This is the most common and robust approach. You can pass a custom flag when initializing the serializer, then adjust the field's read_only attribute during setup:
from rest_framework import serializers from myapp.models import MyModel, RelatedModel class MyModelSerializer(serializers.ModelSerializer): related_model = serializers.HyperlinkedRelatedField( view_name='relatedmodel-detail', queryset=RelatedModel.objects.all() ) def __init__(self, *args, **kwargs): # Pull our custom flag from kwargs before calling the parent constructor make_related_read_only = kwargs.pop('make_related_read_only', False) super().__init__(*args, **kwargs) # Toggle the read-only state based on our flag if make_related_read_only: self.fields['related_model'].read_only = True class Meta: model = MyModel fields = ['id', 'name', 'related_model']
Then, when using the serializer (e.g., in a view), pass the flag to trigger the read-only setting:
# In a function-based view serializer = MyModelSerializer( instance=my_model_instance, context={'request': request}, make_related_read_only=True ) # Or in a class-based view, override get_serializer to set the flag dynamically class MyModelView(generics.RetrieveUpdateAPIView): serializer_class = MyModelSerializer queryset = MyModel.objects.all() def get_serializer(self, *args, **kwargs): # Example: Make it read-only for GET requests, writable for updates kwargs['make_related_read_only'] = self.request.method == 'GET' return super().get_serializer(*args, **kwargs)
2. Conditionally set read-only based on instance state
If you want to make the field read-only only during creation (or only during updates), you can check if the serializer has an existing instance:
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Make the field read-only when creating a new object (no instance exists) if not self.instance: self.fields['related_model'].read_only = True
Why your initial attempt might have failed
Unlike the auto-generated PrimaryKeyRelatedField for model ForeignKeys, HyperlinkedRelatedField sets up its validation and deserialization logic during the serializer's initialization phase. If you tried to set read_only after the serializer was already created (e.g., in a view after initializing it), the field had already locked in its behavior, so the change didn't take effect. By modifying it in __init__, you adjust the field before any validation or serialization processes run.
内容的提问来源于stack exchange,提问作者Piotr

