Added postonly serializer fields

This commit is contained in:
Marc 2014-10-15 21:18:50 +00:00
parent b93ba235b0
commit d817fe7198
11 changed files with 83 additions and 22 deletions

12
TODO.md
View file

@ -135,19 +135,15 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
* multiple domains creation; line separated domains
* Move MU webapps to SaaS?
* DN: Transaction atomicity and backend failure
* SaaS Icons
* offer to create mailbox on account creation
* init.d celery scripts
-# Required-Start: $network $local_fs $remote_fs postgresql celeryd
-# Required-Stop: $network $local_fs $remote_fs postgresql celeryd
* POST only fields (account, username, name) etc
* POST only fields (account, username, name) etc http://inka-labs.com/blog/2013/04/18/post-only-fields-django-rest-framework/
* for list virtual_domains cleaning up we need to know the old domain name when a list changes its address domain, but this is not possible with the current design.
* regenerate virtual_domains every time (configure a separate file for orchestra on postfix)
* update_fields=[] doesn't trigger post save!
* lists -> SaaS ?

View file

@ -235,7 +235,7 @@ class ChangePasswordAdminMixin(object):
def change_password(self, request, id, form_url=''):
if not self.has_change_permission(request):
raise PermissionDenied
# TODO use this insetad of self.get_object()
# TODO use this insetad of self.get_object(), in other places
user = get_object_or_404(self.get_queryset(request), pk=id)
related = []

View file

@ -10,6 +10,55 @@ class SetPasswordSerializer(serializers.Serializer):
widget=widgets.PasswordInput, validators=[validate_password])
from rest_framework.serializers import (HyperlinkedModelSerializerOptions,
HyperlinkedModelSerializer)
class tHyperlinkedModelSerializerOptions(serializers.HyperlinkedModelSerializerOptions):
""" Options for PostHyperlinkedModelSerializer """
def __init__(self, meta):
super(HyperlinkedModelSerializerOptions, self).__init__(meta)
self.postonly_fields = getattr(meta, 'postonly_fields', ())
class HyperlinkedModelSerializer(HyperlinkedModelSerializer):
_options_class = HyperlinkedModelSerializerOptions
def to_native(self, obj):
""" Serialize objects -> primitives. """
ret = self._dict_class()
ret.fields = {}
for field_name, field in self.fields.items():
# Ignore all postonly_fields fron serialization
if field_name in self.opts.postonly_fields:
continue
field.initialize(parent=self, field_name=field_name)
key = self.get_field_key(field_name)
value = field.field_to_native(obj, field_name)
ret[key] = value
ret.fields[key] = field
return ret
def restore_object(self, attrs, instance=None):
model_attrs, post_attrs = {}, {}
for attr, value in attrs.iteritems():
if attr in self.opts.postonly_fields:
post_attrs[attr] = value
else:
model_attrs[attr] = value
obj = super(HyperlinkedModelSerializer, self).restore_object(model_attrs, instance)
# Method to process ignored postonly_fields
self.process_postonly_fields(obj, post_attrs)
return obj
def process_postonly_fields(self, obj, post_attrs):
""" Placeholder method for processing data sent in POST. """
pass
class MultiSelectField(serializers.ChoiceField):
widget = widgets.CheckboxSelectMultiple

View file

@ -2,6 +2,7 @@ from django.forms import widgets
from django.utils.translation import ugettext, ugettext_lazy as _
from rest_framework import serializers
from orchestra.api.serializers import HyperlinkedModelSerializer
from orchestra.apps.accounts.serializers import AccountSerializerMixin
from orchestra.core.validators import validate_password
@ -17,12 +18,14 @@ class RelatedDatabaseUserSerializer(serializers.HyperlinkedModelSerializer):
return DatabaseUser.objects.get(username=data['username'])
class DatabaseSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
class DatabaseSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
users = RelatedDatabaseUserSerializer(many=True, allow_add_remove=True)
# TODO clean user.type = db.type
class Meta:
model = Database
fields = ('url', 'name', 'type', 'users')
postonly_fields = ('name', 'type')
class RelatedDatabaseSerializer(serializers.HyperlinkedModelSerializer):
@ -34,15 +37,17 @@ class RelatedDatabaseSerializer(serializers.HyperlinkedModelSerializer):
return Database.objects.get(name=data['name'])
class DatabaseUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
class DatabaseUserSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
password = serializers.CharField(max_length=128, label=_('Password'),
validators=[validate_password], write_only=True,
widget=widgets.PasswordInput)
databases = RelatedDatabaseSerializer(many=True, allow_add_remove=True, required=False)
# TODO clean user.type = db.type
class Meta:
model = DatabaseUser
fields = ('url', 'username', 'password', 'type', 'databases')
postonly_fields = ('username', 'type')
def save_object(self, obj, **kwargs):
# FIXME this method will be called when saving nested serializers :(

View file

@ -165,6 +165,8 @@ class MySQLBackendMixin(object):
"""mysql mysql -e 'SELECT * FROM db WHERE db="%(name)s";'""" % context, display=False).stdout)
self.assertEqual('', sshrun(self.MASTER_SERVER,
"""mysql mysql -e 'SELECT * FROM user WHERE user="%(username)s";'""" % context, display=False).stdout)
# TODO remove used from database
class RESTDatabaseMixin(DatabaseTestMixin):

View file

@ -1,6 +1,7 @@
from django.core.exceptions import ValidationError
from rest_framework import serializers
from orchestra.api.serializers import HyperlinkedModelSerializer
from orchestra.apps.accounts.serializers import AccountSerializerMixin
from .helpers import domain_for_validation
@ -17,13 +18,14 @@ class RecordSerializer(serializers.ModelSerializer):
return data.get('value')
class DomainSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
class DomainSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
""" Validates if this zone generates a correct zone file """
records = RecordSerializer(required=False, many=True, allow_add_remove=True)
class Meta:
model = Domain
fields = ('url', 'id', 'name', 'records')
fields = ('url', 'name', 'records')
postonly_fields = ('name',)
def full_clean(self, instance):
""" Checks if everything is consistent """

View file

@ -2,15 +2,14 @@ from django.forms import widgets
from django.utils.translation import ugettext, ugettext_lazy as _
from rest_framework import serializers
from orchestra.api.serializers import HyperlinkedModelSerializer
from orchestra.apps.accounts.serializers import AccountSerializerMixin
from orchestra.core.validators import validate_password
from .models import List
# TODO create PasswordSerializerMixin
class ListSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
class ListSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
password = serializers.CharField(max_length=128, label=_('Password'),
validators=[validate_password], write_only=True, required=False,
widget=widgets.PasswordInput)
@ -18,6 +17,7 @@ class ListSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeriali
class Meta:
model = List
fields = ('url', 'name', 'address_name', 'address_domain', 'admin_email')
postonly_fields = ('name',)
def validate_password(self, attrs, source):
""" POST only password """

View file

@ -2,13 +2,14 @@ from django.forms import widgets
from django.utils.translation import ugettext, ugettext_lazy as _
from rest_framework import serializers
from orchestra.api.serializers import HyperlinkedModelSerializer
from orchestra.apps.accounts.serializers import AccountSerializerMixin
from orchestra.core.validators import validate_password
from .models import Mailbox, Address
class MailboxSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
class MailboxSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
password = serializers.CharField(max_length=128, label=_('Password'),
validators=[validate_password], write_only=True, required=False,
widget=widgets.PasswordInput)
@ -18,6 +19,7 @@ class MailboxSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
fields = (
'url', 'name', 'password', 'filtering', 'custom_filtering', 'addresses', 'is_active'
)
postonly_fields = ('name',)
def validate_password(self, attrs, source):
""" POST only password """
@ -54,4 +56,3 @@ class AddressSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
if not attrs['mailboxes'] and not attrs['forward']:
raise serializers.ValidationError("mailboxes or forward addresses should be provided")
return attrs

View file

@ -3,6 +3,7 @@ from django.forms import widgets
from django.utils.translation import ugettext, ugettext_lazy as _
from rest_framework import serializers
from orchestra.api.serializers import HyperlinkedModelSerializer
from orchestra.apps.accounts.serializers import AccountSerializerMixin
from orchestra.core.validators import validate_password
@ -18,7 +19,7 @@ class GroupSerializer(serializers.ModelSerializer):
return SystemUser.objects.get(username=data['username'])
class SystemUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
class SystemUserSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
password = serializers.CharField(max_length=128, label=_('Password'),
validators=[validate_password], write_only=True, required=False,
widget=widgets.PasswordInput)
@ -29,6 +30,7 @@ class SystemUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelS
fields = (
'url', 'username', 'password', 'home', 'shell', 'groups', 'is_active',
)
postonly_fields = ('username',)
def validate_password(self, attrs, source):
""" POST only password """

View file

@ -1,14 +1,16 @@
from rest_framework import serializers
from orchestra.api.fields import OptionField
from orchestra.api.serializers import HyperlinkedModelSerializer
from orchestra.apps.accounts.serializers import AccountSerializerMixin
from .models import WebApp
class WebAppSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
class WebAppSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
options = OptionField(required=False)
class Meta:
model = WebApp
fields = ('url', 'name', 'type', 'options')
postonly_fields = ('name', 'type')

View file

@ -1,6 +1,7 @@
from rest_framework import serializers
from orchestra.api.fields import OptionField
from orchestra.api.serializers import HyperlinkedModelSerializer
from orchestra.apps.accounts.serializers import AccountSerializerMixin
from .models import Website, Content
@ -15,7 +16,7 @@ class ContentSerializer(serializers.HyperlinkedModelSerializer):
return '%s-%s' % (data.get('website'), data.get('path'))
class WebsiteSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
class WebsiteSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
contents = ContentSerializer(required=False, many=True, allow_add_remove=True,
source='content_set')
options = OptionField(required=False)
@ -23,3 +24,4 @@ class WebsiteSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
class Meta:
model = Website
fields = ('url', 'name', 'port', 'domains', 'is_active', 'contents', 'options')
postonly_fileds = ('name',)