1

Using Django Rest Framework 3, Function Based Views, and the ModelSerializer (more specifically the HyperlinkedModelSerializer).

When a user submits a form from the client, I have a view that takes the request data, uses it to call to an external API, then uses the data from the external API to populate data for a model serializer. I believe I have this part working properly, and from what I read, you are supposed to use context and validate()

In my model serializer, I have so far just this one overidden function:

from django.core.validators import URLValidator

def validate(self, data):
    if 'foo_url' in self.context:
        data['foo_url'] = self.context['foo_url']
        URLValidator(data['foo_url'])
    if 'bar_url' in self.context:
        data['bar_url'] = self.context['bar_url']
        URLValidator(data['bar_url'])
    return super(SomeSerializer, self).validate(data)

Just in case, the relevant view code is like so:

context = {'request': request}
...
context['foo_url'] = foo_url
context['bar_url'] = bar_url
s = SomeSerializer(data=request.data, context=context)
if s.is_valid():
    s.save(user=request.user)
    return Response(s.data, status=status.HTTP_201_CREATED)

Now assuming I have the right idea going (my model does populate its foo_url and bar_url fields from the corresponding context data), where I get confused is how the validation is not working. If I give it bad data, the model serializer does not reject it.

I assumed that in validate(), by adding the context data to the data, the data would be checked for validity when is_valid() was called. Maybe not the case, especially when I print out s (after using the serializer but before calling is_valid()) there is no indication that the request object's data has been populated with the context data from validate() (I don't know if it should be).

So I tried calling the URLValidators directly in the validate() method, but still doesn't seem to be working. No errors despite giving it invalid data like 'asdf' or an empty python dict ({}). My test assertions show that the field indeed contains invalid data like '{}'.

What would be the proper way to do this?

LightBlue
  • 137
  • 1
  • 2
  • 8
  • I also don't understand how this also manages to make it past the Django's model validation, since 'asdf' or '{}' are not valid URLField data. – LightBlue Jun 09 '17 at 06:47

1 Answers1

1

You're not calling the validator.

By doing URLValidator(data['bar_url']) you're actually building an url validator with custom schemes (see the docs) and that's it. The proper code should be:

URLValidator()(data['bar_url'])

Where you build a default url validator and then validate the value.


But anyway I would not use this approach, what I would do instead is directly add the extra data (not using the context) and let DRF do the validation by declaring the right fields:

# Somewhere in your view
request.data['bar_url'] = 'some_url'

# In serializer:
class MySerializer(serializers.ModelSerializer):

    bar_url = serializers.URLField()

    class Meta:
        fields = ('bar_url', ...)

To answer your comment

I also don't understand how this also manages to make it past the Django's model validation

See this answer: Why doesn't django's model.save() call full_clean()?

By default Django does not automatically call the .full_clean method so you can save a model instance with invalid values (unless the constraints are on the database level).

Seb D.
  • 5,046
  • 1
  • 28
  • 36
  • Thank you so much, seems like I misread the docs. I thought request.data was inaccessible until after you called is_valid() (sort of like Django form data). I assume Django makes its form data immutable before validation for a reason. Is it generally OK to edit the request data like this? – LightBlue Jun 10 '17 at 00:43
  • 1
    Well, [here](https://stackoverflow.com/questions/12611345/django-why-is-the-request-post-object-immutable) it is explained that there's no strong reasons for the request to be immutable. Personally I think it's ok to add data to the request like that, so that the serialization process can stay the same. – Seb D. Jun 12 '17 at 08:08