Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating nested object #187

Closed
areebbeigh opened this issue Aug 31, 2020 · 7 comments
Closed

Creating nested object #187

areebbeigh opened this issue Aug 31, 2020 · 7 comments

Comments

@areebbeigh
Copy link

areebbeigh commented Aug 31, 2020

Is there a way I can create a nested object without passing the ID of the parent?

e.g

POST /domains/1/nameservers
{
  domain: 1, # -> I don't want to pass this in the request
  <nameserver data>
}
@alanjds
Copy link
Owner

alanjds commented Aug 31, 2020

Yes, but you will need custom code to do it.

On some project, I am using code alike this one:

class NameserverViewset(viewsets.ModelViewSet):
    (...)
    def initialize_request(self, request, *args, **kwargs):
        request = super().initialize_request(request, *args, **kwargs)
        for querydict in [request.data, request.query_params]:
            initial_mutability = getattr(querydict, '_mutable', None)
            if initial_mutability is not None:
                querydict._mutable = True

            querydict['domain'] = kwargs['domain_pk']

            if initial_mutability is not None:
                querydict._mutable = initial_mutability
        return request

However is unclear for me if such code should be provided by drf-nested-routers as a custom viewset superclass or/and if it should be included on the docs.

What is your opinion?

@areebbeigh
Copy link
Author

class NameserverViewset(viewsets.ModelViewSet):
    (...)
    def perform_create(self, serializer):
        serializer.save(user=self.request.user, domain_id=self.kwargs['domain_pk'])

is what I was looking for 😛 thanks.

@areebbeigh
Copy link
Author

areebbeigh commented Aug 31, 2020

However is unclear for me if such code should be provided by drf-nested-routers as a custom viewset superclass or/and if it should be included on the docs.

I'd say mentioning how to get the parent pk is good enough. This looks hacky and breakable.

@nrj
Copy link

nrj commented Sep 9, 2021

Could someone kindly post a complete solution to this with their Serializer and ViewSet please? I am not seeing this work at all. For me the perform_create is never called. The serializer raises before it even gets to the viewset with: {"domain":["This field is required."]}

Here is my Serializer and ViewSet:

class NamespaceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Namespace
        fields = '__all__'

class NamespaceViewSet(viewsets.ModelViewSet):
    serializer_class = NamespaceSerializer

    def perform_create(self, serializer):
        serializer.save(domain_id=self.kwargs['domain_pk'])

So it seems that first the serializer validation must pass before perform_create is called. But if your foreign key is a required field (a very common use case) then the validation won't pass unless your post data contains the parents id.

@areebbeigh
Copy link
Author

Could someone kindly post a complete solution to this with their Serializer and ViewSet please? I am not seeing this work at all. For me the perform_create is never called. The serializer raises before it even gets to the viewset with: {"domain":["This field is required."]}

Here is my Serializer and ViewSet:

class NamespaceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Namespace
        fields = '__all__'

class NamespaceViewSet(viewsets.ModelViewSet):
    serializer_class = NamespaceSerializer

    def perform_create(self, serializer):
        serializer.save(domain=self.kwargs['domain_pk'])

So it seems that first the serializer validation must pass before perform_create is called. But if your foreign key is a required field (a very common use case) then the validation won't pass unless your post data contains the parents id.

I guess you need to set required=False for the domain field in your serializer. The error would be in creating a serializer object if you go through the stack trace.

@nrj
Copy link

nrj commented Sep 9, 2021

Ok I see. Another solution which I've opted for is to just remove the field from the list, since the value is not POSTed I guess this makes sense anyways. Here is the solution for anyone else:

class NamespaceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Namespace
        fields = [field.name for field in model._meta.fields]
        fields.remove('domain')    # don't validate this field

I was originally under the impression that I would get validation of the domain_pk in the url via the serializer:

class NamespaceSerializer(serializers.ModelSerializer):
    # synthesized field validator 
    domain = serializers.PrimaryKeyRelatedField(queryset=Domain.objects.all())

This appears not to be the case. Might be worth mentioning some of this in the docs.

@nrj
Copy link

nrj commented Sep 9, 2021

Since this is somewhat related, is there a recommended way to perform validation of the parent primary key in the URL?

With the above solution, making a POST to say /domains/999/nameservers/ will raise if there is no domain with id=999

Where would it be best to handle this Exception?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants