Добавление поля, которое не относится к модели для сериализатора в структуре Django REST
У меня есть комментарий модели, который при создании может или не может создать нового пользователя. По этой причине для создания нового комментария для моего API требуется поле пароля. Вот моя модель комментариев:
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)
Вот мой вид API:
class CommentNewView(CreateAPIView):
model = Comment
serializer_class = CommentCreateSerializer
Вот мой сериализатор:
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')
Вот ошибка, которую я получаю:
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
Ответы
Ответ 1
Если кому-то интересно, решение должно переопределить метод restore_object и добавить дополнительную экземплярную переменную в объект комментария после ее создания:
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
Ответ 2
Предыдущие ответы не работали над DRF3.0, метод restore_object() теперь устарел.
Решение, которое я использовал, ужасно, но я не нашел лучшего.
Я поместил фиктивный getter/setter для этого поля в модель, это позволяет использовать это поле как любое другое на модели.
Не забудьте установить поле как write_only в определении сериализатора.
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')
Ответ 3
Спасибо за ваш собственный ответ, это мне очень помогло:)
Но я думаю, что это немного более общий, так как я все еще хочу вызвать метод класса super serializer
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
Ответ 4
Что вы можете сделать, это перезаписать функцию pre_save
или create
и вынуть commenter_pw
из полей данных, которые отправляются (не уверен, но вы, вероятно, можете вынуть форму request.POST
или после вы его сериализовали), поэтому структура не должна вызывать ошибки.
Кроме того, если у вас есть дополнительная логика, вы можете реализовать его там, прежде чем сохранять его (например, тот, который проверяет, должен ли пользователь быть создан или что).