I have a model Comment that when created may or may not create a new user. For this reason, my API requires a password field when creating a new comment. Here is my Comment model:
class Comment(models.Model):
    commenter = models.ManyToManyField(Commenter)
    email = models.EmailField(max_length=100)
    author = models.CharField(max_length=100)
    url = models.URLField(max_length=200)
    content = models.TextField(blank=True, null=True)
    ip = models.IPAddressField(max_length=45)
    date = models.DateTimeField(default=datetime.now)
    post_title = models.CharField(max_length=200)
    post_url = models.URLField(max_length=200)
    rating = models.IntegerField(max_length=10, default=0)
Here is my API view:
class CommentNewView(CreateAPIView):
    model = Comment
    serializer_class = CommentCreateSerializer
Here is my serializer:
class CommentCreateSerializer(serializers.ModelSerializer):
    commenter_pw = serializers.CharField(max_length=32, required=False)
    class Meta:
        model = Comment
        fields = ('email', 'author', 'url', 'content', 'ip', 'post_title', 'post_url', 'commenter_pw')
Here is the error I am getting:
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/api/comment/create/
Django Version: 1.5.2
Python Version: 2.7.2
Installed Applications:
('commentflow.apps.dashboard',
 'commentflow.apps.commenter',
 'commentflow.apps.comment',
 'rest_framework',
 'rest_framework.authtoken',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.admin',
 'django.contrib.admindocs')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware')
Traceback:
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  115.                         response = callback(request, *callback_args, **callback_kwargs)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  77.         return view_func(*args, **kwargs)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
  327.             response = self.handle_exception(exc)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
  324.             response = handler(request, *args, **kwargs)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/generics.py" in post
  372.         return self.create(request, *args, **kwargs)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/mixins.py" in create
  50.         if serializer.is_valid():
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/serializers.py" in is_valid
  479.         return not self.errors
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/serializers.py" in errors
  471.                 ret = self.from_native(data, files)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/serializers.py" in from_native
  867.         instance = super(ModelSerializer, self).from_native(data, files)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/serializers.py" in from_native
  324.             return self.restore_object(attrs, instance=getattr(self, 'object', None))
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/rest_framework/serializers.py" in restore_object
  852.             instance = self.opts.model(**attrs)
File "/Users/tlovett1/.virtualenvs/commentflow/lib/python2.7/site-packages/django/db/models/base.py" in __init__
  415.                 raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
Exception Type: TypeError at /api/comment/create/
Exception Value: 'commenter_pw' is an invalid keyword argument for this function
                The ModelSerializer class is the same as a regular Serializer class, except that: It will automatically generate a set of fields for you, based on the model. It will automatically generate validators for the serializer, such as unique_together validators.
Validation in Django REST framework serializers is handled a little differently to how validation works in Django's ModelForm class. With ModelForm the validation is performed partially on the form, and partially on the model instance. With REST framework the validation is performed entirely on the serializer class.
If anyone is curious, the solution is to override the restore_object method and add the extra instance variable to the comment object after it has been instantiated:
def restore_object(self, attrs, instance=None):
        if instance is not None:
            instance.email = attrs.get('email', instance.email)
            instance.author = attrs.get('author', instance.author)
            instance.url = attrs.get('url', instance.url)
            instance.content = attrs.get('content', instance.content)
            instance.ip = attrs.get('ip', instance.ip)
            instance.post_title = attrs.get('post_title', instance.post_title)
            instance.post_url = attrs.get('post_url', instance.post_url)
            return instance
        commenter_pw = attrs.get('commenter_pw')
        del attrs['commenter_pw']
        comment = Comment(**attrs)
        comment.commenter_password = commenter_pw
        return comment
                        Previous answers didn't work on DRF3.0, the restore_object() method is now deprecated.
The solution I have used is awful but I have not found a better one. I have put a dummy getter/setter for this field on the model, this allows to use this field as any other on the model.
Remember to set the field as write_only on serializer definition.
class Comment(models.Model):
    @property
    def commenter_pw():
        return None
    @commenter_pw.setter
    def commenter_pw(self, value):
        pass
class CommentCreateSerializer(serializers.ModelSerializer):
    commenter_pw = serializers.CharField(max_length=32, write_only=True, required=False)
    class Meta:
        model = Comment
        fields = ('email', 'author', 'url', 'content', 'ip', 'post_title', 'post_url', 'commenter_pw')
                        Thanks for your own answer, it helped me a lot :)
But I think this is a bit more generic, since I still want to call the method on the super serializer class
def restore_object(self, attrs, instance=None):
    '''
    we have to ensure that the temporary_password is attached to the model
    even though it is no field
    '''
    commenter_pw = attrs.pop('comment_pw', None)
    obj = super(
        CommentCreateSerializer, self
    ).restore_object(attrs, instance=instance)
    if commenter_pw:
        obj.commenter_pw = commenter_pw
    return obj
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With