Skip to content

Instantly share code, notes, and snippets.

@edulix
Last active February 8, 2020 04:45
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edulix/5311365 to your computer and use it in GitHub Desktop.
Save edulix/5311365 to your computer and use it in GitHub Desktop.
Generic View for listing, adding and removing items in ManyToManyField for django-rest-framework. Comes with an example =)
"""
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2013 Eduardo Robles Elvira <edulix@wadobo.com>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
"""
from __future__ import unicode_literals
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.list import MultipleObjectMixin
from django.core.exceptions import ImproperlyConfigured
from rest_framework import views, mixins, generics, serializers
from rest_framework.settings import api_settings
from rest_framework.response import Response
from rest_framework import status
class CRUDManyToManyView(mixins.ListModelMixin, SingleObjectMixin,
generics.MultipleObjectAPIView):
"""
Generic view that provide CRUD behaviour for ManyToMany fields.
"""
model = None
pk_url_kwarg = 'field_pk'
field_name = ''
model_pk = 'id'
queryset = None
item = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
obj = self.get_object()
item = self.get_item()
getattr(self.get_item(), self.field_name).add(obj)
item = self.get_item().save()
return Response(status=status.HTTP_201_CREATED)
def delete(self, request, *args, **kwargs):
obj = self.get_object()
item = self.get_item()
getattr(self.get_item(), self.field_name).remove(obj)
item = self.get_item().save()
return Response(status=status.HTTP_204_NO_CONTENT)
def get_item(self):
if self.item:
return self.item
if self.model is None:
raise ImproperlyConfigured("%(cls)s is missing a model." % {
'cls': self.__class__.__name__
})
item_view = generics.SingleObjectAPIView()
item_view.model = self.model
item_view.request = self.request
item_view.args = self.args
item_view.kwargs = self.kwargs
self.item = item_view.get_object()
return self.item
def get_queryset(self):
'''
Returns the list of items in model_instance.field_name.all()
'''
if self.queryset:
return self.queryset
if self.field_name is None:
raise ImproperlyConfigured("%(cls)s is missing a field_name." % {
'cls': self.__class__.__name__
})
obj = self.get_item()
self.queryset = getattr(obj, self.field_name).all()
return self.queryset
from django.db import models
class Task(models.Model):
name = models.CharField(max_length=140, blank=False, null=False)
class Project(models.Model):
name = models.CharField(max_length=140, blank=False, null=False)
tasks = models.ManyToManyField(Task, related_name='projects')
from rest_framework import serializers
from models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ('id', 'name')
from django.conf.urls import patterns, url
import views
urlpatterns = patterns(
'',
url(r'^projects/(?P<pk>[0-9]+)/tasks/((?P<field_pk>[0-9]+)/)?$',
views.ProjectTasks.as_view()),
)
from models import Project
from serializers import TaskSerializer
from lib.crudmanytomanyview import CRUDManyToManyView
class ProjectTasks(CRUDManyToManyView):
model = Project
field_name = 'tasks'
serializer_class = TaskSerializer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment