Rest Framework Ember and sideloading - django

I am using Rest Framework Ember along with Django Rest Framework as my JSON API backend for my Ember application.
https://github.com/ngenworks/rest_framework_ember
I have gotten sideloading to work correctly with the resource_name = False flag.
Here is my code below:
class DocumentViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows documents to be viewed or edited.
"""
queryset = Document.objects.all()
serializer_class = DocumentSerializer
# Side loading code for documents
resource_name = False
# renderer_classes = (JSONRenderer, BrowsableAPIRenderer)
def list(self, request, *args, **kwargs):
# import IPython
# IPython.embed()
data = {'document': []}
for doc in self.get_queryset():
data['document'].append(doc)
data['contacts'] = doc.contacts.all()
serializer = DocumentContactSerializer(data)
return Response(serializer.data)
This works as I'd like it to work.
The problem now is that since I've implemented this and overwritten the list() method on the ModelViewSet whenever a new object is created on a POST I am getting this error:
'NoneType' object has no attribute '__getitem__'
If I comment out the resource_name = False then POST works as expected again.
Would you know what could be causing this?

I just ran into the very same problem. Our set-up is also Ember + DRF. And I have found a solution.
You could override the create method like this:
def create(self, request):
self.resource_name = 'document'
data = request.DATA # returns the right querydict now
# do what you want
In this way, you're keeping the side load by using resource_name = false in cases other than create.

Related

Unable to implement django-rules authorization

I'm a new django user looking for some help with django-rules. I'm trying to set up an authorization system that is 'OR' based. I have a 'File' model. I would like only the creator to be able to delete it, but a specific set of users to edit it. I've been able to followed their tutorial and implementation throughout; it works in the shell but not on my site. At the moment no one can delete or update anything.
My view currently looks like:
class FileUpdateView(PermissionRequiredMixin, generics.RetrieveUpdateAPIView):
"""
View updating details of a bill
"""
queryset = File.objects.all()
serializer_class = FileSerializer
permission_required = 'fileupload.change_file'
raise_exception = True
class FileDeleteView(PermissionRequiredMixin, generics.RetrieveDestroyAPIView):
"""
View for deleting a bill
"""
queryset = File.objects.all()
serializer_class = FileSerializer
permission_required = 'fileupload.delete_file'
raise_exception = True
The rules themselves are:
import rules
#rules.predicate
def is_creator(user, file):
"""Checks if user is file's creator"""
return file.owner == user
is_editor = rules.is_group_member('ReadAndWrite')
rules.add_perm('fileupload.change_file', is_editor | is_creator)
rules.add_perm('fileupload.delete_file', is_creator)
I know I'm close I feel like I'm just missing one step.
Thanks in advance!
please check & add settings file authentication backend for Django-rules. Also, you are mixing Django rest permissions with Django-rules permission. you need to check Django-rules permission in Django-rest permissions on view.
in short.
define a custom permission in rest-framework like this.
from rest_framework import permissions
class RulesPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return request.user.has_perm('books.edit_book', obj)
in viewset.
class BookView(viewsets.ModelViewSet):
permission_classes = (RulesPermissions,)
I've been working with the django REST framework and django-rules for a project and found an answer to your question.
The django REST framework uses an API view which is not compatible with rules' views.PermissionRequiredMixin, the authorization workflow and methods called during the API dispatch is different from django's class based view.
Try the following mixin for django REST framework API views and their subclasses:
import six
from django.core.exceptions import ImproperlyConfigured
class PermissionRequiredMixin:
permission_required = None
def get_permission_object(self):
object_getter = getattr(self, 'get_object', lambda: None)
return object_getter()
def get_permission_required(self):
if self.permission_required is None:
raise ImproperlyConfigured(
'{0} is missing the permission_required attribute. Define '
'{0}.permission_required, or override '
'{0}.get_permission_required().'
.format(self.__class__.__name__)
)
if isinstance(self.permission_required, six.string_types):
perms = (self.permission_required, )
else:
perms = self.permission_required
return perms
def check_permissions(self, request):
obj = self.get_permission_object()
user = request.user
missing_permissions = [perm for perm in self.get_permission_required()
if not user.has_perm(perm, obj)]
if any(missing_permissions):
self.permission_denied(
request,
message=('MISSING: {}'.format(', '.join(missing_permissions))))
With this mixin you're not forced to write a REST framework permission for each rules permission.

rest-framework permission testing

I am working with the latest django-rest-framework and want to create some tests. I have a ModelViewSet and a custom permission which accesses request.GET. This all works well, but in my unittest the GET dictionary is empty.
Here is my code:
class MyModelViewSet(ModelViewSet):
...
permission_classes = [IsAuthenticated, CustomPermission]
...
permissions.py:
class CustomPermission(permissions.BasePermission):
def has_permission(self, request, view):
# here I access the GET to check permissions
id = request.GET.get('id')
obj = MyModel.objects.get(id=id)
return request.user == obj.owner
This all works as expected in the browsable api.
But now I wrote a unittest:
class ModelTestCase(APITestCase):
def setUp(self):
self.obj = mommy.make('MyModel')
self.user = mommy.make('CustomUser')
def test_list(self):
self.client.force_authenticate(user=self.user)
url = '%s?id=%s' % (reverse('mymodel-list'), self.obj.id)
r = self.client.get(url) # this raises the exception
And here I get an exception:
models.DoesNotExist: MyModel matching query does not exist.
While debugging I realized that request.GET is empty in has_permission.
Has anyone an idea why this is working in "production" but not in the unittest?
Updating to the newest release (3.2.1) fixed this issue.

django rest framework cannot POST data

I implemented a basic rest api with the django rest framework. It works perfectly using the browsable api or communicating to it with requests. Next step would be submitting data to the rest api.
Here is what I have done so far.
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.AllowAny',),
'PAGINATE_BY': 10
}
[UPDATE:]
models.py
class Request(models.Model):
name = models.TextField()
def save(self, *args, **kwargs):
super(Request, self).save(*args, **kwargs) # Call the "real" save() method.
serializers.py
class RequestSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Request
fields = ('id', 'name')
views.api
class RequestsViewSet(viewsets.ModelViewSet):
queryset = Request.objects.all()
serializer_class = RequestSerializer
Using the browsable api I see that those are the options supported:
Allow: GET, HEAD, OPTIONS
Obviously, POST (and also PUT) is missing.
What I am doing wrong?
Thanks!
Solved it by adding the post method to the modelviewset (in the view):
def post(self, request, format=None):
...
Thanks for helping!
Well, I think you only need to call save method on the model object to persist the object in the database.
First, import model to the view, instantiate a model object in the view, then call save method on the newly created object. If you have model connected to the backend, that will persist your changes.
models.py
class YourModel(models.Model):
name = models.CharField()
views.py
from models import YourModel
def yourView(request):
yourObject = YourModel(name='John')
yourObject.save()
...
Check also Django documentation for models here

Django Haystack Results to Django Rest Framework Serializer

I am attempting to do a search using Django Haystack and then upon retrieving results I need to pass these results to my Django Rest Framework serializer.
The Django Rest Framework serializers.ModelSerializer requires that a queryset of objects gets sent for the serializer to be able to serialize these objects along with their database fields.
When I create my API view and use search to get results haystack returns a searchqueryset.
How could I get this searchqueryset into a django queryset without doing something like:
article_queryset = Article.objects.filter(id__in=[i.object for i in searchqueryset])
As you could imagine, sometimes search can return excess of 1000 search results which means that the above would be very inefficient.
Right now the Django rest framework allows me to paginate my returned objects. I am paginating by 30 objects on each page. How would it be possible for me to do the same with my Haystack searchqueryset?
Any advice or ideas on how to use Haystack along with Django Rest Framework would be great. Examples of how others have done a similar thing would be cool too :)
You could use Haystack's SearchView (instead of a DRF view) which exposes the page object for pagination. Then you can just pass that to your serializer.
E.G. Awhile back I was working on something that required the json object of the rendered results on the current HTML page, so I wrote a filter tag to do this (we were using FacetedSearchView for the UI, and DRF for a more general purpose RESTful API). Called like so in the template:
var articlesJson = {{ page.object_list|article_jsonify }}
Actual filter tag:
import collections
from django.template import Library
from django.utils.safestring import mark_safe
from rest_framework.renderers import JSONRenderer
register = Library()
def article_jsonify(object):
if isinstance(object, collections.Iterable):
many = True
ids = [obj.object.id for obj in object]
else:
many = False
ids = [object.id]
articles = Article.objects.filter(pk__in=ids)
serializer = ArticleSerializer(articles, many=many)
content = JSONRenderer().render(serializer.data)
return mark_safe(content)
register.filter('article_jsonify', article_jsonify)
You could also write a view inherited from generics.ListAPIView and override the get_queryset method, where you would pass the request's query parameter to a SearchQuerySet, then output it using the Serializer. More info here:
http://www.django-rest-framework.org/api-guide/filtering
Of course you might not be able to use the ModelSerializer this way, unless you do something like you mentioned. However DRF has an example on using the paginator on a queryset like so:
http://www.django-rest-framework.org/api-guide/pagination#paginating-querysets
UPDATE
I ended up eventually using a DRF view that uses a Haystack SearchQuerySet as the queryset, and then passing it to a Paginator. Here is a simplified example (I'm sure it can be streamlined some), but I hope it helps someone gets started.
class ArticleList(ListCreateAPIView):
"""
List, Create files
"""
model = Article
def get(self, request, *args, **kwargs):
# simplified filtering of an SQS
q = request.get('q', '')
sqs = SearchQuerySet().filter(content=Clean(q))
paginator = Paginator(sqs, 20)
page = request.QUERY_PARAMS.get('page')
try:
articles = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page
articles = paginator.page(1)
except PageNotAnInteger:
# If page is out of range, deliver last page
articles = paginator.page(paginator.num_pages)
serializer_context = {'request': request}
serializer = PaginatedArticleSerializer(articles, context=serializer_context)
return Response(serializer.data)
class ArticleSerializer(serializers.ModelSerializer):
"""
Base Article Serializer
"""
class Meta:
model = Article
class PaginatedArticleSerializer(pagination.PaginationSerializer):
"""
Serializes page objects of article querysets.
"""
start_index = serializers.SerializerMethodField('get_start_index')
end_index = serializers.SerializerMethodField('get_end_index')
num_pages = serializers.Field(source='paginator.num_pages')
class Meta:
object_serializer_class = ArticleSerializer
def get_start_index(self, page):
return page.start_index()
def get_end_index(self, page):
return page.end_index()
def get_curr_page(self, page):
return page.number

Django Rest Framework bulk updates inserting instead of updating

I'm trying to build out a bulk update view for a specific model using Django Rest Framework. In the short term, it only needs to update one field (toggling an invite from submitted=False to submitted=True), but I'd like it to be able to provide more functionality in the future. Whenever I test the view, however, a new object is being created instead of the current one being modified.
I feel like this must be a simple mistake on my part, but I can't figure out what's going on. The serializer object appears to be ignoring the value for "id" passed in through JSON, which may be contributing to the issue. Current code is:
class InviteBulkUpdateView(generics.UpdateAPIView):
def get_queryset(self):
order = self.kwargs['order']
invite = get_objects_for_user(self.request.user, 'sourcing.view_invite')
return invite.filter(order=order)
serializer_class = InviteInputSerializer
def put(self, request, *args, **kwargs):
data = request.DATA
serializer = InviteInputSerializer(data=data, many=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
class InviteInputSerializer(serializers.ModelSerializer):
class Meta:
model = Invite
fields = ('id', 'order', 'team', 'submitted')
Can anybody shed some light onto what I might be doing wrong?
Just in case somebody is looking for a library to handle this, I wrote a Django-REST-Framework-bulk which allows to do that in a couple of lines (the example only does bulk update but the library also allows bulk create and delete):
from rest_framework_bulk import ListCreateBulkUpdateAPIView
class FooView(ListCreateBulkUpdateAPIView):
model = FooModel
You're not passing object instances to your serializer. (Thus it will create new instances rather than update.) See the docs on dealing with multiple objects in serializers where you'll see your QuerySet passed in.
Django has update method to handle that. You may want to read full info from django documentation.
Here is a sample code where you can use to update given field for multiple records:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import APIException
class Room_Update_ViewSet(APIView):
def put(self, request,*args, **kwargs):
hotel_id = self.kwargs.get('hotel_id')
room_ids = self.request.query_params.get('room_ids')
room_ids = list(map(int, room_ids.split(',')))
try:
Room.objects.filter(hotel_id=hotel_id,id__in=room_ids).update(booked_status=False)
instances = Room.objects.filter(hotel_id=hotel_id,id__in=room_ids)
serializer = RoomSerializer(instance=instances, many=True)
return Response(serializer.data,status=status.HTTP_200_OK)
except Exception as e:
print("Error udating rooms-->",e)
raise APIException