Reputation: 1880
Using the Django REST Framework, I would like to allow users to create and save instances of a Django model through a ListCreateAPIView
(via POST
). One of the fields (a foreign-key field called domain
) shall be determined from a view parameter as defined in urls.py
.
Furthermore, the user can modify the model instance later using PUT
or PATCH
requests to a RetrieveUpdateDestroyAPIView
endpoint (using the same serializer). I don't want the user to be able to modify the domain
field at this point.
While I have the code for the model and the view / serializer structure ready, I'm not sure how to tell the serializer to determine the value of the domain
field based on the view parameter. Here's what I got:
class RRset(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(null=True)
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, related_name='rrsets')
subname = models.CharField(max_length=255, blank=True)
type = models.CharField(max_length=10)
... and a straight-forward ListCreateAPIView
:
class RRsetsDetail(generics.ListCreateAPIView):
serializer_class = RRsetSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
name = self.kwargs['name']
return RRset.objects.filter(domain__name=name, domain__owner=self.request.user.pk)
urls.py
contains the following line:
url(r'^domains/(?P<name>[a-zA-Z\.\-_0-9]+)/rrsets/$', RRsetsDetail.as_view(), name='rrsets')
This allows the user to list and create RRset
objects using the RRsetsSerializer
serializer (the name
field is listed for completeness only, but I do not believe it to be important in this context):
class RRsetSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
def get_name(self, obj):
return '.'.join(filter(None, [obj.subname, obj.domain.name])) + '.' # returns 'subname.name.'
class Meta:
model = RRset
fields = ('created', 'updated', 'domain', 'name', 'type',)
read_only_fields = ('created', 'updated', 'domain', 'type',)
Questions:
domain
name from the view name
parameter?read_only_fields
setting prevents the user from modifying the domain
field later. However, I'm not sure if this setting somehow interacts with the serializer trying to set a default value (can the serializer write the default value, even if read-only is set)?To summarize: What I'm looking for is something like a "write-once field with a default value based on a view parameter".
Upvotes: 4
Views: 1302
Reputation: 16030
I think you are looking for a HiddenField with a combination of CreateOnlyDefault
HiddenField
A field class that does not take a value based on user input, but instead takes its value from a default value or callable.
CreateOnlyDefault
A default class that can be used to only set a default argument during create operations. During updates the field is omitted.
It takes a single argument, which is the default value or callable that should be used during create operations.
And because you want to access the view, you can't just use callable, but you have to use Class-based callable which can have access to a context data.
class DomainDefault(object):
def set_context(self, serializer_field):
view = serializer_field.context['view']
request = serializer_field.context['request']
self.domain = ...#determine the domain based on request+view
def __call__(self):
return self.domain
class RRsetSerializer(serializers.ModelSerializer):
domain = serializers.HiddenField(default=serializers.CreateOnlyDefault(DomainDefault()))
Upvotes: 1