Refactoring base views (update to new viewsets)
parent
9eb35a898a
commit
a022085497
|
@ -1,111 +1,95 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import uuid
|
||||
|
||||
from django.contrib.auth import logout, login, authenticate
|
||||
from django.contrib.auth.views import login as auth_login, logout as auth_logout
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from django import http
|
||||
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
from rest_framework.parsers import JSONParser
|
||||
from rest_framework.reverse import reverse
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework import status
|
||||
from rest_framework import generics
|
||||
from rest_framework import status, generics, viewsets
|
||||
from rest_framework import exceptions as excp
|
||||
|
||||
import django_filters
|
||||
from haystack import query, inputs
|
||||
from djmail.template_mail import MagicMailBuilder
|
||||
|
||||
from greenmine.base.serializers import (LoginSerializer, UserLogged,
|
||||
UserSerializer, RoleSerializer)
|
||||
|
||||
from greenmine.base.serializers import LoginSerializer, UserLogged, UserSerializer, RoleSerializer
|
||||
from greenmine.base.serializers import SearchSerializer
|
||||
from greenmine.base.models import User, Role
|
||||
from greenmine.scrum import models
|
||||
from django.conf import settings
|
||||
|
||||
import django_filters
|
||||
|
||||
|
||||
class ApiRoot(APIView):
|
||||
def get(self, request, format=None):
|
||||
return Response({
|
||||
'login': reverse('login', request=request, format=format),
|
||||
'logout': reverse('logout', request=request, format=format),
|
||||
'projects': reverse('project-list', request=request, format=format),
|
||||
'milestones': reverse('milestone-list', request=request, format=format),
|
||||
'user-stories': reverse('user-story-list', request=request, format=format),
|
||||
'user-stories/statuses': reverse('user-story-status-list', request=request, format=format),
|
||||
'user-stories/points': reverse('points-list', request=request, format=format),
|
||||
'issues/attachments': reverse('issues-attachment-list', request=request, format=format),
|
||||
'issues/statuses': reverse('issues-status-list', request=request, format=format),
|
||||
'issues/types': reverse('issues-type-list', request=request, format=format),
|
||||
'issues': reverse('issues-list', request=request, format=format),
|
||||
'tasks': reverse('tasks-list', request=request, format=format),
|
||||
'tasks/statuses': reverse('tasks-status-list', request=request, format=format),
|
||||
'tasks/attachments': reverse('tasks-attachment-list', request=request, format=format),
|
||||
'severities': reverse('severity-list', request=request, format=format),
|
||||
'priorities': reverse('priority-list', request=request, format=format),
|
||||
'documents': reverse('document-list', request=request, format=format),
|
||||
'questions': reverse('question-list', request=request, format=format),
|
||||
'wiki/pages': reverse('wiki-page-list', request=request, format=format),
|
||||
'users': reverse('user-list', request=request, format=format),
|
||||
'roles': reverse('user-roles', request=request, format=format),
|
||||
'search': reverse('search', request=request, format=format),
|
||||
})
|
||||
|
||||
|
||||
class RoleList(generics.ListCreateAPIView):
|
||||
model = Role
|
||||
class RolesViewSet(viewsets.ViewSet):
|
||||
permission_classes = (IsAuthenticated,)
|
||||
serializer_class = RoleSerializer
|
||||
|
||||
def list(self, request, pk=None):
|
||||
queryset = Role.objects.all()
|
||||
serializer = self.serializer_class(queryset, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def retrieve(self, request, pk=None):
|
||||
role = Role.objects.get(pk=pk)
|
||||
serializer = self.serializer_class(role)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class UsersViewSet(viewsets.ViewSet):
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.all()
|
||||
def get_list_queryset(self):
|
||||
own_projects = (models.Project.objects
|
||||
.filter(members=self.request.user))
|
||||
|
||||
|
||||
class RoleDetail(generics.RetrieveAPIView):
|
||||
model = Role
|
||||
serializer_class = RoleSerializer
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
|
||||
class UserFilter(django_filters.FilterSet):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['is_active']
|
||||
|
||||
|
||||
class UserList(generics.ListCreateAPIView):
|
||||
model = User
|
||||
serializer_class = UserSerializer
|
||||
filter_class = UserFilter
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
def get_queryset(self):
|
||||
projects = models.Project.objects.filter(members=self.request.user)
|
||||
|
||||
#Project filtering
|
||||
project = self.request.QUERY_PARAMS.get('project', None)
|
||||
if project is not None:
|
||||
projects = projects.filter(id=project)
|
||||
own_projects = own_projects.filter(pk=project)
|
||||
|
||||
return super(UserList, self).get_queryset().filter(projects__in=projects)\
|
||||
.order_by('id').distinct()
|
||||
queryset = (User.objects.filter(projects__in=own_projects)
|
||||
.order_by('username').distinct())
|
||||
|
||||
def pre_save(self, obj):
|
||||
pass
|
||||
return queryset
|
||||
|
||||
def list(self, request, pk=None):
|
||||
queryset = self.get_list_queryset()
|
||||
serializer = UserSerializer(queryset, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
model = User
|
||||
serializer_class = UserSerializer
|
||||
permission_classes = (IsAuthenticated,)
|
||||
def retrieve(self, request, pk=None):
|
||||
return Response({})
|
||||
|
||||
import uuid
|
||||
from django.db.models import Q
|
||||
from djmail.template_mail import MagicMailBuilder
|
||||
@action(methods=["POST"], permission_classes=[])
|
||||
def login(self, request, pk=None):
|
||||
username = request.DATA.get('username', None)
|
||||
password = request.DATA.get('password', None)
|
||||
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
return Response({"detail": "Invalid username or password"},
|
||||
status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class RecoveryPassword(APIView):
|
||||
def post(self, request):
|
||||
if not user.check_password(password):
|
||||
return Response({"detail": "Invalid username or password"},
|
||||
status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
user = authenticate(username=username, password=password)
|
||||
login(request, user)
|
||||
|
||||
serializer = UserSerializer(user)
|
||||
response_data = serializer.data
|
||||
response_data["token"] = request.session.session_key
|
||||
|
||||
return Response(response_data)
|
||||
|
||||
@action(methods=["POST"], permission_classes=[])
|
||||
def password_recovery(self, request, pk=None):
|
||||
username_or_email = request.DATA.get('username', None)
|
||||
|
||||
if not username_or_email:
|
||||
|
@ -126,63 +110,31 @@ class RecoveryPassword(APIView):
|
|||
|
||||
return Response({"detail": "Mail sended successful!"})
|
||||
|
||||
|
||||
class Login(APIView):
|
||||
def post(self, request, format=None):
|
||||
username = request.DATA.get('username', None)
|
||||
password = request.DATA.get('password', None)
|
||||
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
if user.check_password(password):
|
||||
user = authenticate(username=username, password=password)
|
||||
login(request, user)
|
||||
|
||||
return_data = LoginSerializer(UserLogged(**{
|
||||
'token': request.session.session_key,
|
||||
'username': request.user.username,
|
||||
'first_name': request.user.first_name,
|
||||
'last_name': request.user.last_name,
|
||||
'email': request.user.email,
|
||||
'last_login': request.user.last_login,
|
||||
'color': request.user.color,
|
||||
'description': request.user.description,
|
||||
'default_language': request.user.default_language,
|
||||
'default_timezone': request.user.default_timezone,
|
||||
'colorize_tags': request.user.colorize_tags,
|
||||
}))
|
||||
|
||||
return Response(return_data.data)
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
return Response({"detail": "Invalid username or password"}, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
|
||||
|
||||
class Logout(APIView):
|
||||
def post(self, request, format=None):
|
||||
@action(methods=["GET", "POST"])
|
||||
def logout(self, request, pk=None)
|
||||
logout(request)
|
||||
return Response()
|
||||
|
||||
|
||||
class Search(APIView):
|
||||
def get(self, request, format=None):
|
||||
class Search(viewsets.ViewSet):
|
||||
def list(self, request, **kwargs):
|
||||
text = request.QUERY_PARAMS.get('text', None)
|
||||
project = request.QUERY_PARAMS.get('project', None)
|
||||
project_id = request.QUERY_PARAMS.get('project', None)
|
||||
|
||||
if text and project:
|
||||
#TODO: permission check
|
||||
queryset = query.SearchQuerySet()
|
||||
queryset = queryset.filter(text=inputs.AutoQuery(text))
|
||||
queryset = queryset.filter(project_id=project)
|
||||
|
||||
return_data = SearchSerializer(queryset)
|
||||
return Response(return_data.data)
|
||||
|
||||
return Response({"detail": "Parameter text can't be empty"}, status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
project = self._get_project(project_id)
|
||||
except models.Project.DoesNotExist:
|
||||
raise excp.PermissionDenied({"detail": "Wrong project id"})
|
||||
|
||||
queryset = query.SearchQuerySet()
|
||||
queryset = queryset.filter(text=inputs.AutoQuery(text))
|
||||
queryset = queryset.filter(project_id=project_id)
|
||||
|
||||
return_data = SearchSerializer(queryset)
|
||||
return Response(return_data.data)
|
||||
|
||||
def _get_project(self, project_id):
|
||||
own_projects = (models.Project.objects
|
||||
.filter(members=self.request.user))
|
||||
|
||||
return own_projects.get(pk=project_id)
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2011 Andrei Antoukh <niwi@niwi.be>
|
||||
# License: BSD-3
|
||||
|
||||
from django.db import models
|
||||
from base64 import b64encode, b64decode
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("niwi")
|
||||
|
||||
|
||||
class DictField(models.Field):
|
||||
""" Dictionary pickled field. """
|
||||
__metaclass__ = models.SubfieldBase
|
||||
__prefix__ = "pickle_dict::"
|
||||
__pickleproto__ = -1
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, dict):
|
||||
return value
|
||||
if isinstance(value, (str, unicode)) and value.startswith(self.__prefix__):
|
||||
local_value = value[len(self.__prefix__):]
|
||||
return pickle.loads(b64decode(str(local_value)))
|
||||
else:
|
||||
return {}
|
||||
|
||||
def get_db_prep_value(self, value, connection, prepared=False):
|
||||
if value is not None:
|
||||
if isinstance(value, dict):
|
||||
value = self.__prefix__ + b64encode(pickle.dumps(value, protocol=self.__pickleproto__))
|
||||
else:
|
||||
raise TypeError('This field can only store dictionaries.')
|
||||
|
||||
return value
|
||||
|
||||
def get_internal_type(self):
|
||||
return 'TextField'
|
||||
|
||||
def value_to_string(self, obj):
|
||||
if not obj:
|
||||
return ""
|
||||
|
||||
value = getattr(obj, self.attname)
|
||||
assert isinstance(value, dict)
|
||||
return self.__prefix__ + b64encode(pickle.dumps(value, protocol=self.__pickleproto__))
|
||||
return self.token.join(map(unicode, value))
|
||||
|
||||
def south_field_triple(self):
|
||||
from south.modelsinspector import introspector
|
||||
field_class = "django.db.models.fields.TextField"
|
||||
args, kwargs = introspector(self)
|
||||
return (field_class, args, kwargs)
|
||||
|
||||
|
||||
class ListField(models.Field):
|
||||
""" Pickled list field. """
|
||||
__metaclass__ = models.SubfieldBase
|
||||
__prefix__ = "pickle_list::"
|
||||
__pickleproto__ = -1
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, (list, tuple)):
|
||||
return value
|
||||
|
||||
if isinstance(value, (str, unicode)) and value.startswith(self.__prefix__):
|
||||
local_value = value[len(self.__prefix__):]
|
||||
return pickle.loads(b64decode(str(local_value)))
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_db_prep_value(self, value, connection, prepared=False):
|
||||
if value is not None:
|
||||
if isinstance(value, (list, tuple)):
|
||||
value = self.__prefix__ + b64encode(pickle.dumps(value, protocol=self.__pickleproto__))
|
||||
else:
|
||||
raise TypeError('This field can only store list or tuple objects')
|
||||
|
||||
return value
|
||||
|
||||
def get_internal_type(self):
|
||||
return 'TextField'
|
||||
|
||||
def value_to_string(self, obj):
|
||||
if not obj:
|
||||
return ""
|
||||
|
||||
value = getattr(obj, self.attname)
|
||||
assert isinstance(value, (list, tuple))
|
||||
return self.__prefix__ + b64encode(pickle.dumps(value, protocol=self.__pickleproto__))
|
||||
return self.token.join(map(unicode, value))
|
||||
|
||||
def south_field_triple(self):
|
||||
from south.modelsinspector import introspector
|
||||
field_class = "django.db.models.fields.TextField"
|
||||
args, kwargs = introspector(self)
|
||||
return (field_class, args, kwargs)
|
||||
|
||||
|
||||
class CSVField(models.TextField):
|
||||
__metaclass__ = models.SubfieldBase
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.token = kwargs.pop('token', ',')
|
||||
super(CSVField, self).__init__(*args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
if not value:
|
||||
return
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return value.split(self.token)
|
||||
|
||||
def get_db_prep_value(self, value):
|
||||
if not value:
|
||||
return
|
||||
assert(isinstance(value, list) or isinstance(value, tuple))
|
||||
return self.token.join([unicode(s) for s in value])
|
||||
|
||||
def value_to_string(self, obj):
|
||||
value = self._get_val_from_obj(obj)
|
||||
return self.get_db_prep_value(value)
|
||||
|
||||
def south_field_triple(self):
|
||||
from south.modelsinspector import introspector
|
||||
field_class = "django.db.models.fields.TextField"
|
||||
args, kwargs = introspector(self)
|
||||
return (field_class, args, kwargs)
|
|
@ -18,6 +18,7 @@ class GreenmineSessionMiddleware(SessionMiddleware):
|
|||
request.session = engine.SessionStore(session_key)
|
||||
|
||||
|
||||
|
||||
COORS_ALLOWED_ORIGINS = getattr(settings, 'COORS_ALLOWED_ORIGINS', '*')
|
||||
COORS_ALLOWED_METHODS = getattr(settings, 'COORS_ALLOWED_METHODS',
|
||||
['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE', 'PATCH'])
|
||||
|
|
|
@ -47,6 +47,9 @@ class User(WatcherMixin, AbstractUser):
|
|||
verbose_name=_('colorize tags'))
|
||||
objects = UserManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ["username"]
|
||||
|
||||
|
||||
class Role(models.Model):
|
||||
name = models.CharField(max_length=200, null=False, blank=False,
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from rest_framework.urlpatterns import format_suffix_patterns
|
||||
from rest_framework import routers
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
from greenmine.base import api
|
||||
|
||||
# Special router for actions.
|
||||
actions_router = routers.Route(url=r'^{prefix}/actions/{methodname}{trailing_slash}$',
|
||||
mapping={'{httpmethod}': '{methodname}'},
|
||||
name='{basename}-{methodnamehyphen}',
|
||||
initkwargs={})
|
||||
|
||||
urlpatterns = format_suffix_patterns(patterns('',
|
||||
url(r'^auth/login/$', api.Login.as_view(), name='login'),
|
||||
url(r'^auth/logout/$', api.Logout.as_view(), name='logout'),
|
||||
url(r'^users/$', api.UserList.as_view(), name="user-list"),
|
||||
url(r'^users/(?P<pk>[0-9]+)/$', api.UserDetail.as_view(), name="user-detail"),
|
||||
url(r'^roles/$', api.RoleList.as_view(), name="roles"),
|
||||
url(r'^roles/(?P<pk>[0-9]+)/$', api.RoleDetail.as_view(), name='role-detail'),
|
||||
url(r'^search/$', api.Search.as_view(), name="search"),
|
||||
url(r'^$', api.ApiRoot.as_view(), name='api_root'),
|
||||
))
|
||||
router = routers.DefaultRouter(trailing_slash=False)
|
||||
router.routes.append(actions_router)
|
||||
router.register("users", api.UsersViewSet, base_name="users")
|
||||
router.register("roles", api.RolesViewSet, base_name="roles")
|
||||
router.register("search", api.Search, base_name="search")
|
||||
|
||||
urlpatterns = router.urls
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from picklefield.fields import PickledObjectField
|
||||
from greenmine.base.utils.slug import slugify_uniquely as slugify
|
||||
from greenmine.base.fields import DictField
|
||||
|
||||
|
||||
class Document(models.Model):
|
||||
|
@ -27,7 +27,7 @@ class Document(models.Model):
|
|||
attached_file = models.FileField(max_length=1000, null=True, blank=True,
|
||||
upload_to='documents',
|
||||
verbose_name=_('attached_file'))
|
||||
tags = DictField(null=False, blank=True,
|
||||
tags = PickledObjectField(null=False, blank=True,
|
||||
verbose_name=_('tags'))
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -6,7 +6,6 @@ from django.utils import timezone
|
|||
from django.dispatch import receiver
|
||||
|
||||
from greenmine.base.utils.slug import slugify_uniquely, ref_uniquely
|
||||
from greenmine.base.fields import DictField
|
||||
from greenmine.scrum.models import Project
|
||||
|
||||
from picklefield.fields import PickledObjectField
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.contrib import admin
|
|||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^api/', include('greenmine.base.urls')),
|
||||
url(r'^api/core/', include('greenmine.base.urls')),
|
||||
url(r'^api/scrum/', include('greenmine.scrum.urls')),
|
||||
url(r'^api/documents/', include('greenmine.documents.urls')),
|
||||
url(r'^api/questions/', include('greenmine.questions.urls')),
|
||||
|
|
|
@ -16,7 +16,7 @@ git+git://github.com/toastdriven/django-haystack.git
|
|||
django-picklefield==0.3.0
|
||||
django-reversion==1.7
|
||||
django-sampledatahelper==0.0.1
|
||||
djangorestframework==2.2.5
|
||||
djangorestframework==2.3.6
|
||||
gunicorn==17.5
|
||||
kombu==2.5.12
|
||||
mimeparse==0.1.3
|
||||
|
|
Loading…
Reference in New Issue