Added postonly serializer fields
This commit is contained in:
parent
b93ba235b0
commit
d817fe7198
12
TODO.md
12
TODO.md
|
@ -135,19 +135,15 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
||||||
* multiple domains creation; line separated domains
|
* multiple domains creation; line separated domains
|
||||||
* Move MU webapps to SaaS?
|
* Move MU webapps to SaaS?
|
||||||
|
|
||||||
* DN: Transaction atomicity and backend failure
|
|
||||||
|
|
||||||
* SaaS Icons
|
|
||||||
|
|
||||||
* offer to create mailbox on account creation
|
* offer to create mailbox on account creation
|
||||||
|
|
||||||
* init.d celery scripts
|
* init.d celery scripts
|
||||||
-# Required-Start: $network $local_fs $remote_fs postgresql celeryd
|
-# Required-Start: $network $local_fs $remote_fs postgresql celeryd
|
||||||
-# Required-Stop: $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.
|
* 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!
|
* update_fields=[] doesn't trigger post save!
|
||||||
|
|
||||||
|
* lists -> SaaS ?
|
||||||
|
|
|
@ -235,7 +235,7 @@ class ChangePasswordAdminMixin(object):
|
||||||
def change_password(self, request, id, form_url=''):
|
def change_password(self, request, id, form_url=''):
|
||||||
if not self.has_change_permission(request):
|
if not self.has_change_permission(request):
|
||||||
raise PermissionDenied
|
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)
|
user = get_object_or_404(self.get_queryset(request), pk=id)
|
||||||
|
|
||||||
related = []
|
related = []
|
||||||
|
|
|
@ -10,6 +10,55 @@ class SetPasswordSerializer(serializers.Serializer):
|
||||||
widget=widgets.PasswordInput, validators=[validate_password])
|
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):
|
class MultiSelectField(serializers.ChoiceField):
|
||||||
widget = widgets.CheckboxSelectMultiple
|
widget = widgets.CheckboxSelectMultiple
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.forms import widgets
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from orchestra.api.serializers import HyperlinkedModelSerializer
|
||||||
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
||||||
from orchestra.core.validators import validate_password
|
from orchestra.core.validators import validate_password
|
||||||
|
|
||||||
|
@ -17,12 +18,14 @@ class RelatedDatabaseUserSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
return DatabaseUser.objects.get(username=data['username'])
|
return DatabaseUser.objects.get(username=data['username'])
|
||||||
|
|
||||||
|
|
||||||
class DatabaseSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
class DatabaseSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
||||||
users = RelatedDatabaseUserSerializer(many=True, allow_add_remove=True)
|
users = RelatedDatabaseUserSerializer(many=True, allow_add_remove=True)
|
||||||
|
# TODO clean user.type = db.type
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Database
|
model = Database
|
||||||
fields = ('url', 'name', 'type', 'users')
|
fields = ('url', 'name', 'type', 'users')
|
||||||
|
postonly_fields = ('name', 'type')
|
||||||
|
|
||||||
|
|
||||||
class RelatedDatabaseSerializer(serializers.HyperlinkedModelSerializer):
|
class RelatedDatabaseSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
@ -34,15 +37,17 @@ class RelatedDatabaseSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
return Database.objects.get(name=data['name'])
|
return Database.objects.get(name=data['name'])
|
||||||
|
|
||||||
|
|
||||||
class DatabaseUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
class DatabaseUserSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
||||||
password = serializers.CharField(max_length=128, label=_('Password'),
|
password = serializers.CharField(max_length=128, label=_('Password'),
|
||||||
validators=[validate_password], write_only=True,
|
validators=[validate_password], write_only=True,
|
||||||
widget=widgets.PasswordInput)
|
widget=widgets.PasswordInput)
|
||||||
databases = RelatedDatabaseSerializer(many=True, allow_add_remove=True, required=False)
|
databases = RelatedDatabaseSerializer(many=True, allow_add_remove=True, required=False)
|
||||||
|
# TODO clean user.type = db.type
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DatabaseUser
|
model = DatabaseUser
|
||||||
fields = ('url', 'username', 'password', 'type', 'databases')
|
fields = ('url', 'username', 'password', 'type', 'databases')
|
||||||
|
postonly_fields = ('username', 'type')
|
||||||
|
|
||||||
def save_object(self, obj, **kwargs):
|
def save_object(self, obj, **kwargs):
|
||||||
# FIXME this method will be called when saving nested serializers :(
|
# FIXME this method will be called when saving nested serializers :(
|
||||||
|
|
|
@ -165,6 +165,8 @@ class MySQLBackendMixin(object):
|
||||||
"""mysql mysql -e 'SELECT * FROM db WHERE db="%(name)s";'""" % context, display=False).stdout)
|
"""mysql mysql -e 'SELECT * FROM db WHERE db="%(name)s";'""" % context, display=False).stdout)
|
||||||
self.assertEqual('', sshrun(self.MASTER_SERVER,
|
self.assertEqual('', sshrun(self.MASTER_SERVER,
|
||||||
"""mysql mysql -e 'SELECT * FROM user WHERE user="%(username)s";'""" % context, display=False).stdout)
|
"""mysql mysql -e 'SELECT * FROM user WHERE user="%(username)s";'""" % context, display=False).stdout)
|
||||||
|
|
||||||
|
# TODO remove used from database
|
||||||
|
|
||||||
|
|
||||||
class RESTDatabaseMixin(DatabaseTestMixin):
|
class RESTDatabaseMixin(DatabaseTestMixin):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from orchestra.api.serializers import HyperlinkedModelSerializer
|
||||||
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
||||||
|
|
||||||
from .helpers import domain_for_validation
|
from .helpers import domain_for_validation
|
||||||
|
@ -17,13 +18,14 @@ class RecordSerializer(serializers.ModelSerializer):
|
||||||
return data.get('value')
|
return data.get('value')
|
||||||
|
|
||||||
|
|
||||||
class DomainSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
class DomainSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
||||||
""" Validates if this zone generates a correct zone file """
|
""" Validates if this zone generates a correct zone file """
|
||||||
records = RecordSerializer(required=False, many=True, allow_add_remove=True)
|
records = RecordSerializer(required=False, many=True, allow_add_remove=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Domain
|
model = Domain
|
||||||
fields = ('url', 'id', 'name', 'records')
|
fields = ('url', 'name', 'records')
|
||||||
|
postonly_fields = ('name',)
|
||||||
|
|
||||||
def full_clean(self, instance):
|
def full_clean(self, instance):
|
||||||
""" Checks if everything is consistent """
|
""" Checks if everything is consistent """
|
||||||
|
|
|
@ -2,15 +2,14 @@ from django.forms import widgets
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from orchestra.api.serializers import HyperlinkedModelSerializer
|
||||||
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
||||||
from orchestra.core.validators import validate_password
|
from orchestra.core.validators import validate_password
|
||||||
|
|
||||||
from .models import List
|
from .models import List
|
||||||
|
|
||||||
|
|
||||||
# TODO create PasswordSerializerMixin
|
class ListSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
||||||
|
|
||||||
class ListSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
|
||||||
password = serializers.CharField(max_length=128, label=_('Password'),
|
password = serializers.CharField(max_length=128, label=_('Password'),
|
||||||
validators=[validate_password], write_only=True, required=False,
|
validators=[validate_password], write_only=True, required=False,
|
||||||
widget=widgets.PasswordInput)
|
widget=widgets.PasswordInput)
|
||||||
|
@ -18,6 +17,7 @@ class ListSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeriali
|
||||||
class Meta:
|
class Meta:
|
||||||
model = List
|
model = List
|
||||||
fields = ('url', 'name', 'address_name', 'address_domain', 'admin_email')
|
fields = ('url', 'name', 'address_name', 'address_domain', 'admin_email')
|
||||||
|
postonly_fields = ('name',)
|
||||||
|
|
||||||
def validate_password(self, attrs, source):
|
def validate_password(self, attrs, source):
|
||||||
""" POST only password """
|
""" POST only password """
|
||||||
|
|
|
@ -2,13 +2,14 @@ from django.forms import widgets
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from orchestra.api.serializers import HyperlinkedModelSerializer
|
||||||
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
||||||
from orchestra.core.validators import validate_password
|
from orchestra.core.validators import validate_password
|
||||||
|
|
||||||
from .models import Mailbox, Address
|
from .models import Mailbox, Address
|
||||||
|
|
||||||
|
|
||||||
class MailboxSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
class MailboxSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
||||||
password = serializers.CharField(max_length=128, label=_('Password'),
|
password = serializers.CharField(max_length=128, label=_('Password'),
|
||||||
validators=[validate_password], write_only=True, required=False,
|
validators=[validate_password], write_only=True, required=False,
|
||||||
widget=widgets.PasswordInput)
|
widget=widgets.PasswordInput)
|
||||||
|
@ -18,6 +19,7 @@ class MailboxSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
|
||||||
fields = (
|
fields = (
|
||||||
'url', 'name', 'password', 'filtering', 'custom_filtering', 'addresses', 'is_active'
|
'url', 'name', 'password', 'filtering', 'custom_filtering', 'addresses', 'is_active'
|
||||||
)
|
)
|
||||||
|
postonly_fields = ('name',)
|
||||||
|
|
||||||
def validate_password(self, attrs, source):
|
def validate_password(self, attrs, source):
|
||||||
""" POST only password """
|
""" POST only password """
|
||||||
|
@ -54,4 +56,3 @@ class AddressSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
|
||||||
if not attrs['mailboxes'] and not attrs['forward']:
|
if not attrs['mailboxes'] and not attrs['forward']:
|
||||||
raise serializers.ValidationError("mailboxes or forward addresses should be provided")
|
raise serializers.ValidationError("mailboxes or forward addresses should be provided")
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from django.forms import widgets
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from orchestra.api.serializers import HyperlinkedModelSerializer
|
||||||
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
||||||
from orchestra.core.validators import validate_password
|
from orchestra.core.validators import validate_password
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ class GroupSerializer(serializers.ModelSerializer):
|
||||||
return SystemUser.objects.get(username=data['username'])
|
return SystemUser.objects.get(username=data['username'])
|
||||||
|
|
||||||
|
|
||||||
class SystemUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
class SystemUserSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
||||||
password = serializers.CharField(max_length=128, label=_('Password'),
|
password = serializers.CharField(max_length=128, label=_('Password'),
|
||||||
validators=[validate_password], write_only=True, required=False,
|
validators=[validate_password], write_only=True, required=False,
|
||||||
widget=widgets.PasswordInput)
|
widget=widgets.PasswordInput)
|
||||||
|
@ -29,6 +30,7 @@ class SystemUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelS
|
||||||
fields = (
|
fields = (
|
||||||
'url', 'username', 'password', 'home', 'shell', 'groups', 'is_active',
|
'url', 'username', 'password', 'home', 'shell', 'groups', 'is_active',
|
||||||
)
|
)
|
||||||
|
postonly_fields = ('username',)
|
||||||
|
|
||||||
def validate_password(self, attrs, source):
|
def validate_password(self, attrs, source):
|
||||||
""" POST only password """
|
""" POST only password """
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from orchestra.api.fields import OptionField
|
from orchestra.api.fields import OptionField
|
||||||
|
from orchestra.api.serializers import HyperlinkedModelSerializer
|
||||||
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
||||||
|
|
||||||
from .models import WebApp
|
from .models import WebApp
|
||||||
|
|
||||||
|
|
||||||
class WebAppSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
class WebAppSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
||||||
options = OptionField(required=False)
|
options = OptionField(required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WebApp
|
model = WebApp
|
||||||
fields = ('url', 'name', 'type', 'options')
|
fields = ('url', 'name', 'type', 'options')
|
||||||
|
postonly_fields = ('name', 'type')
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from orchestra.api.fields import OptionField
|
from orchestra.api.fields import OptionField
|
||||||
|
from orchestra.api.serializers import HyperlinkedModelSerializer
|
||||||
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
from orchestra.apps.accounts.serializers import AccountSerializerMixin
|
||||||
|
|
||||||
from .models import Website, Content
|
from .models import Website, Content
|
||||||
|
@ -15,7 +16,7 @@ class ContentSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
return '%s-%s' % (data.get('website'), data.get('path'))
|
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,
|
contents = ContentSerializer(required=False, many=True, allow_add_remove=True,
|
||||||
source='content_set')
|
source='content_set')
|
||||||
options = OptionField(required=False)
|
options = OptionField(required=False)
|
||||||
|
@ -23,3 +24,4 @@ class WebsiteSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Website
|
model = Website
|
||||||
fields = ('url', 'name', 'port', 'domains', 'is_active', 'contents', 'options')
|
fields = ('url', 'name', 'port', 'domains', 'is_active', 'contents', 'options')
|
||||||
|
postonly_fileds = ('name',)
|
||||||
|
|
Loading…
Reference in a new issue