I have two model classes as follows that are related to each other with User class.
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
first_name = models.CharField(max_length=30, null=False, blank=False)
last_name = models.CharField(max_length=30, null=False, blank=False)
father_name = models.CharField(max_length=30, null=False, blank=False)
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
code = models.CharField(max_length=15, null=True, blank=True)
And now a request comes as follows:
http://localhost:8000/api/v1/search/users/?first_name=john&&last_name=dev&&code=25
How can I search if one of the input parameters is in one of the tables (person or company)?
The effort I have made but no result found:
class SearchUserAPI(APIView):
def get(self, request, format=None):
try:
from django.db.models import Q
q = request.query_params
search_models = [Person, Company]
search_results = []
for model in search_models:
fields = [x for x in model._meta.fields if isinstance(x, django.db.models.CharField)]
search_queries = [Q({x.name + "__icontains": q.get(x.name)}) for x in fields]
print(search_queries)
q_object = Q()
for query in search_queries:
q_object = q_object | query
results = model.objects.filter(q_object)
search_results.append(results)
data = [search_results]
return Response(data, status=status.HTTP_200_OK)
except Exception as e:
return Response({"error": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
thats will better and will work i hope )
class SearchUserAPI(APIView):
def get(self, request, format=None):
try:
q = request.query_params
first_name = q.get('first_name')
last_name = q.get('last_name')
code = q.get('code')
data = Person.objects.filter(first_name=first_name, last_name=last_name, company_code=code)
data = [search_results]
return Response(data, status=status.HTTP_200_OK)
except Exception as e:
return Response({"error": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
also you can easy add another fields to filter
you can use filterset in drf
I assume your user is somehow linked with person or company in Foreign key or One to one
from django_filters import rest_framework as filters, BaseInFilter
class SearchFilter(filters.FilterSet):
first_name = filters.CharFilter(
field_name="person__first_name",
label='person name'
)
last_name = filters.CharFilter(
field_name="person__first_name",
label='person name'
)
code = = filters.NumberFilter(
field_name="company__code",
label='code'
)
class Meta:
model = User
fields = ['first_name','last_name','code']
You can simply call your searchfilter set in views now It will do your work
Related
models.py
#
from django.db import models
from user.models import User
from chat.models import TradeChatRoom, AuctionChatRoom
class Goods(models.Model):
class Meta:
db_table = 'Goods'
ordering = ['-created_at'] # 일단 추가해뒀습니다
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sell_goods')
buyer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='buy_goods', null=True)
trade_room = models.ForeignKey(TradeChatRoom, on_delete=models.CASCADE)
auction_room = models.ForeignKey(AuctionChatRoom, on_delete=models.CASCADE)
title = models.CharField(max_length=256)
content = models.TextField()
category = models.CharField(max_length=32)
status = models.BooleanField(null=True)
predict_price = models.IntegerField()
start_price = models.IntegerField()
high_price = models.IntegerField(null=True)
start_date = models.DateField(null = True)
start_time = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(User, related_name='like_goods', null=True)
class GoodsImage(models.Model):
class Meta:
db_table = "GoodsImage"
goods = models.ForeignKey(Goods, on_delete=models.CASCADE)
image = models.ImageField(upload_to='goods/')
serializer.py
from rest_framework import serializers
from .models import Goods,GoodsImage
class GoodImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(use_url=True)
def get_image(self, obj):
image = obj.goods_set.all()
return GoodsPostSerializer(instance=image, many = True, context = self.context)
class Meta:
model = GoodsImage
field =('image',)
class GoodsPostSerializer(serializers.ModelSerializer):
image = GoodImageSerializer(many=True, read_only = True)
class Meta:
model = Goods
fields = (
'seller', 'buyer','auction_room','title','content',
'category','status','predict_price','start_price','high_price',
'trade_room','start_date','start_time','created_at','like','image',
)
read_only_fields = ("seller",)
def create(self, validated_data):
goods = Goods.objects.create(**validated_data)
images_data = self.context['request'].FILES
for image_date in images_data.getlist('image'):
GoodsImage.objects.create(goods = goods, image = image_date)
return goods
error
images_data = self.context['request'].FILES
KeyError: 'request'
I want to save multiple images, but I keep getting an error. I don't know what to do anymore.
I searched for a method and followed it, but it seems that I am the only one who gets an error.
Please help if you know how to solve this problem.
And I want to know if it is correct to put it in a list like "image":["12.jpeg,"13.jpeg] when inserting multiple images through postman.
It's hard not being able to solve this problem. please help me if you know the answer
Change GoodImageSerializer calling this:
GoodImageSerializer(instance=images, many = True, context={'request': request})
Then change your GoodsPostSerializer's create method like this:
def get_image(self, obj):
image = obj.goods_set.all()
request = self.context['request']
return GoodsPostSerializer(instance=image, many = True, context={'request': request})
I can not get a clear answer after two days of searching for what must probably be one of the most common things to do with a DRF:
I have the following model:
class ProcessedStockAmounts(models.Model):
prodName = models.ForeignKey(Productlist, on_delete=models.CASCADE, blank=False, unique=False)
amount = models.CharField(unique=False, max_length=255)
time = models.ForeignKey(StockTakingTimes, on_delete=models.CASCADE, blank=False, unique=False, default=1)
def __str__(self):
return str(self.prodName)
And I am returning a JSON object via my API that looks like this:
[{'prodName': 'SV1', 'amount': '1111111', 'time' : 1}]
When I insert my prodName with a value it has no problem, but obviously my user will not know the prodName ID and only the prod name. So when I try to insert the above I get the following error:
ValueError: Cannot assign "'SV1'": "ProcessedStockAmounts.prodName" must be a "Productlist" instance.
This was the closest I got to an answer and when I do the following it actually inserts:
p = ProcessedStockAmounts(amount='33', prodName = Productlist.objects.get(productid = 'SV1'), time = StockTakingTimes.objects.get(times='06:00'))
p.save()
but giving data this way is obviously defeating the purpose.
My serializer looks like the following:
class TestSerializer(serializers.ModelSerializer):
# time = serializers.SlugRelatedField(read_only=True, slug_field='time')
prodName = serializers.CharField()
# prodName = serializers.SlugRelatedField(read_only=True, slug_field='prodName')
class Meta:
model = ProcessedStockAmounts
fields = ('prodName','amount','time')
With my view:
class InsertMultiProcessedStock(APIView):
def post(self, request, format='json'):
serializer = TestSerializer(data = request.data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
Productlist model:
class Productlist(models.Model):
productid = models.CharField(unique=True, max_length=20) # Field name made lowercase.
proddescription = models.CharField(db_column='prodDescription', max_length=255, blank=True, null=True) # Field name made lowercase.
packaging = models.ForeignKey(Packaging, on_delete=models.CASCADE, blank=True, null=True)
unitweight = models.FloatField(db_column='unitWeight', blank=True, null=True)
def __str__(self):
return self.productid
This would have been easier if you had the related model. But the commented-out slugrelatedfield is the way you should do it, using the actual field name:
prodName = serializers.SlugRelatedField(read_only=False, slug_field='productid')
Your serializer is wrong, You must use relationship serializer.
prodName = ProductlistSerializer(many = False)
But I found Your model defintion is very confusing
I have created an Employee class below in my models. The Employee class has multiple Foreign Keys such as User,Contact,Skill etc. I would like to make it possible that when I create an Employee all the other objects will be created plus including the User object. I have implemented a POST method in my view that does this but I feel like my code is too long. How do I make a single POST to create all these multiple objects? An illustration using Managers will be also nice.
class Employee(models.Model):
"""
Model, which holds general information of an employee.
"""
user = models.OneToOneField(settings.AUTH_USER_MODEL,
related_name='employees', null=True)
company = models.ForeignKey(
'hr.Company',
verbose_name='Company',
related_name='companies',
null=True, blank=True,
)
hr_number = models.CharField(
verbose_name='HR number',
blank=True, null=True,
max_length=20, unique=True
)
identification_number = models.CharField(
verbose_name='ID Number',
blank=True, null=True,
max_length=20, unique=True
)
contract_type = models.ForeignKey(Contract)
tax_id_number = models.CharField(
max_length=20, null=True, verbose_name='Tax ID', blank=True, unique=True)
skill = models.ForeignKey(Skill)
# joining can be added in user profile
joining_date = models.DateField(null=True, verbose_name="Joining Date")
job_title = models.ForeignKey(
Job, related_name='job_titles', null=True, blank=True, help_text='Default Permission for different modules in Portal depends upon employee\'s Designation.')
department = models.ForeignKey(
Department, related_name='department', null=True, blank=True, on_delete=models.SET_NULL)
is_manager = models.BooleanField(default=False)
# leave_count = models.IntegerField(default=0)
active = models.BooleanField(default=True)
In my views I have have implemented the POST method below:
class AddEmployee(APIView):
# permission_classes = (permissions.DjangoObjectPermissions,)
# serializer_class = EmployeeSerializer
"""
{
"user":null,
"new_user":{
"first_name":"John",
"last_name":"Wane",
"username":"Wai",
"email":"jwane#gmail.com",
"password":"123"
},
"company":1,
"department":1,
"identification_number":"234567",
"hr_number":"GH/099/2017",
"tax_id_number":"AEEEEEE",
"joining_date":"2018-04-02",
"job_title":null,
"new_job":{
"name":"Doctor",
"min_salary":50000,
"max_salary":50000
}
}
"""
def post(self, request, format=None):
try:
company = Company.objects.get(id=request.data['company'])
department = Department.objects.get(id=request.data['department'])
try:
c_user = User.objects.get(id=request.data['user'])
except:
new_user = request.data['new_user']
c_user = User.objects.create(first_name=new_user['first_name'],
last_name=new_user['last_name'],
username=new_user['username'],
email=new_user['email'],
password=new_user['password'])
try:
job_title = Job.objects.get(id=request.data['job_title'])
except:
new_job = request.data['new_job']
if new_job:
job_title = Job.objects.create(
name=new_job['name'],
min_salary=new_job['min_salary'],
max_salary=new_job['max_salary']
)
employee = Employee.objects.create(
user=c_user,
company=company,
department=department,
job_title=job_title,
hr_number=request.data['hr_number'],
identification_number=request.data['identification_number'],
tax_id_number=request.data['tax_id_number'],
joining_date=request.data['joining_date']
)
except Exception as e:
print(e)
return Response(status=status.HTTP_201_CREATED)
DRF do not manage nested serialiser or this kind of things. that said, you can simplify your code using Model.objects.get_or_create
example :
try:
job_title = Job.objects.get(id=request.data['job_title'])
except:
new_job = request.data['new_job']
if new_job:
job_title = Job.objects.create(
name=new_job['name'],
min_salary=new_job['min_salary'],
max_salary=new_job['max_salary']
)
# can be write with get_or_create:
job_defaults = {
'name': new_job['name'],
'min_salary': new_job['min_salary'],
'max_salary': new_job['max_salary']
}
Job.objects.get_or_create(name=new_job['name'],defaults=job_defaults)
You can also use Model Serializer to manage filtering + validating + save sub object
examples :
# serializers.py
class JobSerializer(serializers.ModelSerializer):
class Meta:
model = Job
fields = ('id', 'name', 'min_salary', 'max_salary')
# inside views.py's post method
try:
job_title = Job.objects.get(id=request.data['job_title'])
except:
JobSerializer(data=new_job).save()
see also:
DRF documentation about writable nested serializers
External extension to help with nested serializers
I'm creating a Django (1.8) webapp that saves racing laptimes and scoreboards. The database is populated using an API built using Django Rest Framework. It's the first time I'm trying to build a proper api using rest framework.
A quick overview of the models:
Event, A racing event/weekend
Session, A single race/practice/quali - FK Event
Car, A car taking part in a session - FK Session
Lap, Laps for specific car - FK Car
The Event is created manually, but the rest is supposed to be "dynamic" (get or create)
Right now I'm trying to create a new car using my API, but I'm stuck. To get the cars event and session I'm trying to use the url;
/api/results/skrotbilsracet-29042016/r1/cars/
The idea is to post data to this url and "get or create" a new car object.
To get the correct session object for the new car session FK, I need to use a custom function that takes the kwargs and tries to find the session.
The more I read about how to solve this, the more confused I get.
Could someone push me in the right direction?
This is my latest attempt at solving this, which just gives me "{"session":["This field is required."]}"
models.py
class Session(models.Model):
session_types = (
('p', 'Practice'),
('q', 'Qualification'),
('r', 'Race')
)
event_id = models.ForeignKey(Event, related_name='sessions')
name = models.CharField(max_length=2, blank=True)
current_session = models.BooleanField(default=True)
session_type = models.CharField(max_length=2,
choices=session_types)
started = models.DateTimeField(auto_now_add=True)
ended = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['started']
def save(self):
if not self.name:
# Get number of sessions
session_count = Session.objects.filter(event_id=self.event_id)\
.filter(session_type=self.session_type)\
.count()
session_count += 1
self.name = self.session_type + str(session_count)
super(Session, self).save()
def __unicode__(self):
string = self.started.strftime("%d-%m-%Y %H:%M") + ' - '
string += self.name.upper()
return(string)
class Car(models.Model):
session = models.ForeignKey(Session, related_name='cars')
number = models.IntegerField()
full_name = models.CharField(max_length=256, blank=True)
short_name = models.CharField(max_length=256, blank=True)
race_class = models.CharField(max_length=50, blank=True)
best_lap = models.IntegerField(blank=True, null=True)
best_lap_time = models.CharField(max_length=20, blank=True)
best_sector1 = models.CharField(max_length=20, blank=True)
best_sector2 = models.CharField(max_length=20, blank=True)
best_sector3 = models.CharField(max_length=20, blank=True)
best_speed = models.IntegerField(blank=True, null=True)
pitstops = models.IntegerField(blank=True, null=True)
total_time = models.CharField(max_length=20, blank=True)
transponder = models.CharField(max_length=50, blank=True)
apiUrls.py
urlpatterns = [
url(r'^raceslug/$', raceSlugView.as_view(), name='race-slug'),
url(r'^events/$', eventsView.as_view(), name='event-list'),
url(r'^session/$', getSessionView.as_view(), name='session-pk'),
url(r'^(?P<event_id>[a-z0-9\-]+)/$', eventView.as_view(), name='event-detail'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/$', sessionView.as_view(), name='session-detail'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/cars/$', carsView.as_view(), name='car-list'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/(?P<number>[0-9]+)/$', carView.as_view(), name='car-detail'),
]
urlpatterns = format_suffix_patterns(urlpatterns)
api.py
class carsView(generics.ListCreateAPIView):
serializer_class = carSerializer
def get_session(self, event_id, name):
print('Getting session')
# Get event object
try:
event = Event.objects.get(event_id=event_id)
print('Found event')
except ObjectDoesNotExist:
print('Did not find event')
return
# Get session object
try:
session = event.sessions.get(name=name)
print('Found session: ', session)
return session
except ObjectDoesNotExist:
print('Did not find session')
return
def get_queryset(self):
print('Getting queryset')
print('event_id: ' + self.kwargs['event_id'])
print('name: ' + self.kwargs['name'])
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
return(Car.objects.filter(session=session.pk))
def perform_create(self, serializer):
print('Creating new car')
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
serializer.save(session=session)
serializers.py
class carSerializer(serializers.ModelSerializer):
laps = lapSerializer(many=True, read_only=True)
class Meta:
model = Car
fields = (
'session',
'number',
'full_name',
'short_name',
'race_class',
'best_lap',
'best_lap_time',
'best_sector1',
'best_sector2',
'best_sector3',
'best_speed',
'pitstops',
'total_time',
'transponder',
'laps')
Solution:
This is what I actually changed to get it working.
api.py
from rest_framework.serializers import ValidationError
class carsView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
print('Creating new car')
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
number = self.request.POST.get('number')
car = session.cars.filter(number=number)
if car.exists():
raise ValidationError('Car already exists')
serializer.save(session=session)
serializers.py
class carSerializer(serializers.ModelSerializer):
laps = lapSerializer(many=True, read_only=True)
session = serializers.StringRelatedField(required=False)
...
I see that you're creating your session ID there:
def get_queryset(self):
...
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
return(Car.objects.filter(session=session.pk))
Then you don't need it in a serializer, only in a model. So you can set it a snot required in a serializer, but it will still be required in a model.
I guess this answer could help you: Django REST Framework serializer field required=false
I try to update a channel:
PUT
content [{'url': 'http://localhost:8000/api/movies/2', 'title': u'Ariel', 'backdrop_path': u'/z2QUexmccqrvw1kDMw3R8TxAh5E.jpg', 'popularity': 0.082, 'poster_path': u'/8ld3BEg8gnynRsfj2AzbLocD8NR.jpg', 'release_date': datetime.date(1988, 10, 21), 'runtime': 69L, 'tagline': u'', 'vote_average': 9.0, 'vote_count': 0L}]
csrfmiddlewaretoken XXXXXXXXXXXXXXXXXXXXXXXXXXx
name cody private
owner http://localhost:8000/api/users/1
private 1
And I get this error:
instance should be a queryset or other iterable with many=True
And here is the code you need to understand what's going on.
class Channel(models.Model):
"""
A channel is a "container" for a users movies and television shows.
"""
PUBLIC_VISIBILITY, PRIVATE_VISIBILITY = 0, 1
VISIBILITY_CHOICES = (
(PUBLIC_VISIBILITY, 'public'),
(PRIVATE_VISIBILITY, 'private'),
)
owner = models.ForeignKey(User, related_name='owned_channels')
name = models.CharField(max_length=60)
content = models.ManyToManyField(Movie, db_table='channel_contents',
related_name='channels', null=True, blank=True, default=None)
subscribers = models.ManyToManyField(User, db_table='channel_subscribers',
related_name='subscribed_channels', null=True, blank=True, default=None)
created = models.DateTimeField(auto_now_add=True)
last_mod = models.DateTimeField(auto_now=True)
query = models.CharField(max_length=255, default='')
private = models.IntegerField(choices=VISIBILITY_CHOICES, default=PRIVATE_VISIBILITY)
default = models.BooleanField(default=False)
class Movie(models.Model):
id = models.BigIntegerField(primary_key=True)
adult = models.BooleanField()
backdrop_path = models.ImageField(upload_to='backdrop/')
budget = models.IntegerField(blank=True, null=True)
genres = models.ManyToManyField('Genre',
through='MovieGenre',
blank=True, null=True)
homepage = models.URLField(blank=True, null=True)
imdb_id = models.CharField(max_length=20, blank=True, null=True)
original_title = models.CharField(max_length=100)
overview = models.TextField(blank=True, null=True)
popularity = models.FloatField(blank=True, null=True)
poster_path = models.ImageField(upload_to='poster/')
release_date = models.DateField(blank=True, null=True)
revenue = models.IntegerField(blank=True, null=True)
runtime = models.IntegerField(blank=True, null=True)
tagline = models.CharField(max_length=200, blank=True, null=True)
title = models.CharField(max_length=100, db_index=True)
vote_average = models.FloatField(blank=True, null=True)
vote_count = models.IntegerField(blank=True, null=True)
actors = models.ManyToManyField('Actor',
through='MovieActor',
blank=True, null=True)
directors = models.ManyToManyField('Director',
through='MovieDirector',
blank=True, null=True)
production_companies = models.ManyToManyField(
'ProductionCompany',
through='MovieProduction',
blank=True, null=True)
Channel serializing code:
# Routes
url(r'^channels$', ChannelList.as_view(), name='channel-list'),
url(r'^channels/(?P<pk>\d+)$', ChannelDetail.as_view(), name='channel-detail'),
# Views
class ChannelList(generics.ListCreateAPIView):
"""
API endpoint that represents a list of users.
"""
model = Channel
serializer_class = ChannelSerializer
class ChannelDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint that represents a single users.
"""
model = Channel
serializer_class = ChannelSerializer
# Serializer
class ChannelSerializer(serializers.HyperlinkedModelSerializer):
content = MovieSerializer(many=True)
class Meta:
model = Channel
fields = ('url', 'owner', 'name', 'content', 'private')
As you can read here, nested relations currently don't support write operations. Use HyperlinkedRelatedField instead or write a custom serializer, that implements the features you need.
If you want to update the nested relation you can do like this,
class SchoolSerializer(serializers.HyperlinkedModelSerializer):
students = StudentSerializer(many=True, read_only=True)
students_ids = serializers.PrimaryKeyRelatedField(many=True,\
read_only=False, queryset=Student.objects.all(),\
source='students')
class Meta:
model = School
fields = ('name', 'image', 'address', 'url',\
'students', 'students_ids')
use PrimaryKeyRelatedField this will allow you to create, update, nested relations (Many-to-Many field) by just passing a list of id's
students will give you nested data,
students_ids can be used for write operations
This is a little outdated, but for future people looking for a potential solution to this problem, I found it useful to patch viewset.
You cannot read post params twice, which is the only thing preventing one from passing a Primary key for the related update and performing the m2m update in post_save
I made a custom viewset based on ModelViewSet with updated create and update statements:
In your app, you can create a module called viewsets.py:
# -*- coding: utf-8 -*-
from rest_framework import mixins
from rest_framework import status
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
class RelatedCreateModelMixin(mixins.CreateModelMixin):
'''
Monkey patch the UpdateModel for ModelViewSet Mixin to support data
transferrance from pre - to - save - to - post
'''
def create(self, request, *args, **kwargs):
data = request.DATA
serializer = self.get_serializer(data=data, files=request.FILES)
if serializer.is_valid():
self.pre_save(serializer.object, data=data)
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True, data=data)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RelatedUpdateModelMixin(mixins.UpdateModelMixin):
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
self.object = self.get_object_or_none()
data = request.DATA
serializer = self.get_serializer(self.object, data=data,
files=request.FILES, partial=partial)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
try:
self.pre_save(serializer.object, data=data)
except ValidationError as err:
# full_clean on model instance may be called in pre_save,
# so we have to handle eventual errors.
return Response(err.message_dict, status=status.HTTP_400_BAD_REQUEST)
if self.object is None:
self.object = serializer.save(force_insert=True)
self.post_save(self.object, data=data, created=True)
return Response(serializer.data, status=status.HTTP_201_CREATED)
self.object = serializer.save(force_update=True)
self.post_save(self.object, created=False)
return Response(serializer.data, status=status.HTTP_200_OK)
class RelatedModelViewSet(RelatedCreateModelMixin,
mixins.RetrieveModelMixin,
RelatedUpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass
Then, in your view, use instead:
from MYAPP import viewsets
Which allows you to do something along the lines of:
def post_save(self, obj, *args, **kwargs):
data = kwargs.get('data')
model_id = data.get('id')
parent_obj = Model.objects.get(id=model_id)
method = self.request.method
if method == 'POST':
parent_obj.m2m.add(obj)
elif method == 'PUT':
parent_obj.m2m.remove(obj)
Not the most elegant solution, but I find it preferable to writing a custom serializer