Fixes on REST API
This commit is contained in:
parent
774422a41b
commit
f984d28709
4
TODO.md
4
TODO.md
|
@ -131,7 +131,9 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
||||||
|
|
||||||
* AccountAdminMixin auto adds 'account__name' on searchfields and handle account_link on fieldsets
|
* AccountAdminMixin auto adds 'account__name' on searchfields and handle account_link on fieldsets
|
||||||
|
|
||||||
* Separate panel from server passwords? Store passwords on panel?
|
* Separate panel from server passwords? Store passwords on panel? set_password special backend operation?
|
||||||
|
|
||||||
|
* be more explicit about which backends are resources and which are service handling
|
||||||
|
|
||||||
|
|
||||||
* What fields we really need on contacts? name email phone and what more?
|
* What fields we really need on contacts? name email phone and what more?
|
||||||
|
|
|
@ -22,18 +22,17 @@ class APIRoot(views.APIView):
|
||||||
singleton_pk = getattr(viewset, 'singleton_pk', False)
|
singleton_pk = getattr(viewset, 'singleton_pk', False)
|
||||||
if singleton_pk:
|
if singleton_pk:
|
||||||
url_name = detail_name.format(basename=basename)
|
url_name = detail_name.format(basename=basename)
|
||||||
kwargs = { 'pk': singleton_pk(viewset(), request) }
|
kwargs = {
|
||||||
|
'pk': singleton_pk(viewset(), request)
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
url_name = list_name.format(basename=basename)
|
url_name = list_name.format(basename=basename)
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
url = reverse(url_name, request=request, format=format, kwargs=kwargs)
|
url = reverse(url_name, request=request, format=format, kwargs=kwargs)
|
||||||
links.append('<%s>; rel="%s"' % (url, url_name))
|
links.append('<%s>; rel="%s"' % (url, url_name))
|
||||||
# Add user link
|
headers = {
|
||||||
url_name = detail_name.format(basename='user')
|
'Link': ', '.join(links)
|
||||||
kwargs = { 'pk': request.user.pk }
|
}
|
||||||
url = reverse(url_name, request=request, format=format, kwargs=kwargs)
|
|
||||||
links.append('<%s>; rel="%s"' % (url, url_name))
|
|
||||||
headers = { 'Link': ', '.join(links) }
|
|
||||||
body = {
|
body = {
|
||||||
name.lower(): getattr(settings, name, None) for name in self.names
|
name.lower(): getattr(settings, name, None) for name in self.names
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class AccountViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = super(AccountViewSet, self).get_queryset()
|
qs = super(AccountViewSet, self).get_queryset()
|
||||||
return qs.filter(id=self.request.user)
|
return qs.filter(id=self.request.user.pk)
|
||||||
|
|
||||||
|
|
||||||
router.register(r'accounts', AccountViewSet)
|
router.register(r'accounts', AccountViewSet)
|
||||||
|
|
|
@ -62,8 +62,8 @@ class Account(auth.AbstractBaseUser):
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
created = not self.pk
|
created = not self.pk
|
||||||
super(Account, self).save(*args, **kwargs)
|
super(Account, self).save(*args, **kwargs)
|
||||||
if created and hasattr(self, 'groups'):
|
if created and hasattr(self, 'systemgroups'):
|
||||||
self.groups.create(name=self.username, account=self)
|
self.systemgroups.create(name=self.username, account=self)
|
||||||
|
|
||||||
def send_email(self, template, context, contacts=[], attachments=[], html=None):
|
def send_email(self, template, context, contacts=[], attachments=[], html=None):
|
||||||
contacts = self.contacts.filter(email_usages=contacts)
|
contacts = self.contacts.filter(email_usages=contacts)
|
||||||
|
|
|
@ -7,7 +7,7 @@ class AccountSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Account
|
model = Account
|
||||||
fields = (
|
fields = (
|
||||||
'url', 'username', 'type', 'language', 'register_date', 'is_active'
|
'url', 'username', 'type', 'language', 'date_joined', 'is_active'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,6 @@ class BillSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeriali
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Bill
|
model = Bill
|
||||||
fields = (
|
fields = (
|
||||||
'url', 'number', 'bill_type', 'status', 'created_on', 'due_on',
|
'url', 'number', 'type', 'total', 'is_sent', 'created_on', 'due_on',
|
||||||
'comments', 'html', 'lines'
|
'comments', 'html', 'lines'
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,7 +29,7 @@ class TicketViewSet(viewsets.ModelViewSet):
|
||||||
qs = super(TicketViewSet, self).get_queryset()
|
qs = super(TicketViewSet, self).get_queryset()
|
||||||
qs = qs.select_related('creator', 'queue')
|
qs = qs.select_related('creator', 'queue')
|
||||||
qs = qs.prefetch_related('messages__author')
|
qs = qs.prefetch_related('messages__author')
|
||||||
return qs.filter(creator__account=self.request.user.account_id)
|
return qs.filter(creator=self.request.user)
|
||||||
|
|
||||||
|
|
||||||
class QueueViewSet(mixins.ListModelMixin,
|
class QueueViewSet(mixins.ListModelMixin,
|
||||||
|
|
|
@ -18,10 +18,10 @@ class AddressSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeri
|
||||||
|
|
||||||
def get_fields(self, *args, **kwargs):
|
def get_fields(self, *args, **kwargs):
|
||||||
fields = super(AddressSerializer, self).get_fields(*args, **kwargs)
|
fields = super(AddressSerializer, self).get_fields(*args, **kwargs)
|
||||||
account = self.context['view'].request.user.account_id
|
account = self.context['view'].request.user.pk
|
||||||
mailboxes = fields['mailboxes'].queryset
|
mailboxes = fields['mailboxes'].queryset
|
||||||
fields['mailboxes'].queryset = mailboxes.filter(account=account)
|
fields['mailboxes'].queryset = mailboxes.filter(account=account)
|
||||||
# TODO do it on permissions or in self.filter_by_account_field ?
|
# TODO do it on permissions or in self.filter_by_account_field ?
|
||||||
domain = fields['domain'].queryset
|
domain = fields['domain'].queryset
|
||||||
fields['domain'].queryset = domain .filter(account=account)
|
fields['domain'].queryset = domain.filter(account=account)
|
||||||
return fields
|
return fields
|
||||||
|
|
|
@ -6,13 +6,13 @@ from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import wrap_admin_view
|
from orchestra.admin.utils import wrap_admin_view
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin
|
from orchestra.apps.accounts.admin import SelectAccountAdminMixin
|
||||||
|
|
||||||
from .forms import UserCreationForm, UserChangeForm
|
from .forms import UserCreationForm, UserChangeForm
|
||||||
from .models import SystemUser
|
from .models import SystemUser
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
class SystemUserAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
list_display = ('username', 'account_link', 'shell', 'home', 'is_active',)
|
list_display = ('username', 'account_link', 'shell', 'home', 'is_active',)
|
||||||
list_filter = ('is_active', 'shell')
|
list_filter = ('is_active', 'shell')
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
@ -32,7 +32,7 @@ class SystemUserAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
search_fields = ['username']
|
search_fields = ['username']
|
||||||
readonly_fields = ('is_main', 'account_link',)
|
readonly_fields = ('account_link',)
|
||||||
change_readonly_fields = ('username',)
|
change_readonly_fields = ('username',)
|
||||||
filter_horizontal = ('groups',)
|
filter_horizontal = ('groups',)
|
||||||
filter_by_account_fields = ('groups',)
|
filter_by_account_fields = ('groups',)
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from orchestra.api import router, SetPasswordApiMixin
|
from orchestra.api import router, SetPasswordApiMixin
|
||||||
from orchestra.apps.accounts.api import AccountApiMixin
|
from orchestra.apps.accounts.api import AccountApiMixin
|
||||||
|
|
||||||
from .serializers import UserSerializer
|
from .models import SystemUser
|
||||||
|
from .serializers import SystemUserSerializer
|
||||||
|
|
||||||
|
|
||||||
class UserViewSet(AccountApiMixin, SetPasswordApiMixin, viewsets.ModelViewSet):
|
class SystemUserViewSet(AccountApiMixin, SetPasswordApiMixin, viewsets.ModelViewSet):
|
||||||
model = get_user_model()
|
model = SystemUser
|
||||||
serializer_class = UserSerializer
|
serializer_class = SystemUserSerializer
|
||||||
|
|
||||||
|
|
||||||
router.register(r'users', UserViewSet)
|
router.register(r'systemusers', SystemUserViewSet)
|
||||||
|
|
|
@ -29,10 +29,9 @@ class SystemUser(models.Model):
|
||||||
help_text=_("Home directory relative to account's ~main_user"))
|
help_text=_("Home directory relative to account's ~main_user"))
|
||||||
shell = models.CharField(_("shell"), max_length=32,
|
shell = models.CharField(_("shell"), max_length=32,
|
||||||
choices=settings.USERS_SHELLS, default=settings.USERS_DEFAULT_SHELL)
|
choices=settings.USERS_SHELLS, default=settings.USERS_DEFAULT_SHELL)
|
||||||
groups = models.ManyToManyField('systemusers.Group', blank=True,
|
groups = models.ManyToManyField('systemusers.SystemGroup', blank=True,
|
||||||
help_text=_("A new group will be created for the user. "
|
help_text=_("A new group will be created for the user. "
|
||||||
"Which additional groups would you like them to be a member of?"))
|
"Which additional groups would you like them to be a member of?"))
|
||||||
is_main = models.BooleanField(_("is main"), default=False)
|
|
||||||
is_active = models.BooleanField(_("active"), default=True,
|
is_active = models.BooleanField(_("active"), default=True,
|
||||||
help_text=_("Designates whether this account should be treated as active. "
|
help_text=_("Designates whether this account should be treated as active. "
|
||||||
"Unselect this instead of deleting accounts."))
|
"Unselect this instead of deleting accounts."))
|
||||||
|
@ -54,7 +53,7 @@ class SystemUser(models.Model):
|
||||||
created = not self.pk
|
created = not self.pk
|
||||||
super(SystemUser, self).save(*args, **kwargs)
|
super(SystemUser, self).save(*args, **kwargs)
|
||||||
if created:
|
if created:
|
||||||
self.groups.get_or_create(name=self.username, account=self.account)
|
self.groups.create(name=self.username, account=self.account)
|
||||||
|
|
||||||
def set_password(self, raw_password):
|
def set_password(self, raw_password):
|
||||||
self.password = make_password(raw_password)
|
self.password = make_password(raw_password)
|
||||||
|
@ -63,13 +62,13 @@ class SystemUser(models.Model):
|
||||||
return self.account.is_active and self.is_active
|
return self.account.is_active and self.is_active
|
||||||
|
|
||||||
|
|
||||||
class Group(models.Model):
|
class SystemGroup(models.Model):
|
||||||
name = models.CharField(_("name"), max_length=64, unique=True,
|
name = models.CharField(_("name"), max_length=64, unique=True,
|
||||||
help_text=_("Required. 30 characters or fewer. Letters, digits and ./-/_ only."),
|
help_text=_("Required. 30 characters or fewer. Letters, digits and ./-/_ only."),
|
||||||
validators=[validators.RegexValidator(r'^[\w.-]+$',
|
validators=[validators.RegexValidator(r'^[\w.-]+$',
|
||||||
_("Enter a valid group name."), 'invalid')])
|
_("Enter a valid group name."), 'invalid')])
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
||||||
related_name='groups')
|
related_name='systemgroups')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -6,17 +6,19 @@ from rest_framework import serializers
|
||||||
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 SystemUser
|
||||||
|
|
||||||
class UserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
|
|
||||||
|
class SystemUserSerializer(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)
|
||||||
|
groups = serializers.RelatedField(many=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = get_user_model()
|
model = SystemUser
|
||||||
fields = (
|
fields = (
|
||||||
'url', 'username', 'password', 'first_name', 'last_name', 'email',
|
'url', 'username', 'password', 'home', 'shell', 'groups', 'is_active',
|
||||||
'is_admin', 'is_active',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_password(self, attrs, source):
|
def validate_password(self, attrs, source):
|
||||||
|
@ -32,4 +34,4 @@ class UserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSeriali
|
||||||
# FIXME this method will be called when saving nested serializers :(
|
# FIXME this method will be called when saving nested serializers :(
|
||||||
if not obj.pk:
|
if not obj.pk:
|
||||||
obj.set_password(obj.password)
|
obj.set_password(obj.password)
|
||||||
super(UserSerializer, self).save_object(obj, **kwargs)
|
super(SystemUserSerializer, self).save_object(obj, **kwargs)
|
||||||
|
|
Loading…
Reference in a new issue