Django Rest Framework 将对象附加到多对多字段而不删除前一个
Django Rest Framework append object to many to many field without deleting the previous one
我是 DRF 的新手,不知道如何在不删除前一个对象的情况下将对象附加到 多对多 字段一.
我正在使用 PATCH 来更新字段 MONITORS,但是之前的值被实际值所取代。我要追加。
API GET
是:
{
"id": 1,
"created": "2018-05-02T23:43:07.605000Z",
"modified": "2021-04-03T10:25:12.280896Z",
"companies_house_id": "",
"name": "Ellison PLC",
"description": "",
"date_founded": "2018-04-28",
"country": 4,
"creator": 7,
"monitors": [
3
]
}
在 PATCH {"monitors":[11]}
之后我得到:
{
"id": 1,
"created": "2018-05-02T23:43:07.605000Z",
"modified": "2021-04-03T10:25:12.280896Z",
"companies_house_id": "",
"name": "Ellison PLC",
"description": "",
"date_founded": "2018-04-28",
"country": 4,
"creator": 7,
"monitors": [
11
]
}
我希望最终的 GET API 是 "monitors": [3, 11]
models.py
class Company(TimeStampedModel):
companies_house_id = models.CharField(max_length=8, blank=True)
name = models.CharField(max_length=200, blank=True)
description = models.TextField(blank=True)
date_founded = models.DateField(null=True, blank=True)
country = models.ForeignKey(Country, on_delete=models.PROTECT, blank=True)
creator = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
related_name='companies_created'
)
monitors = models.ManyToManyField(
settings.AUTH_USER_MODEL,
blank=True,
related_name='companies_monitored',
help_text='Users who want to be notified of updates to this company'
)
def __unicode__(self):
return u'{0}'.format(self.name)
serializers.py
from rest_framework import serializers
from .models import Company
from django.contrib.auth import get_user_model
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
companies_monitored = CompanySerializer(many=True, read_only=True)
# companies_moniotred = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = get_user_model()
fields = ('id', 'username', 'companies_monitored')
views.py
class CompanyDetailsView(generics.RetrieveUpdateAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
class UserList(generics.ListCreateAPIView):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
def get_object(self, username):
username = get_object_or_404(get_user_model(), username=username)
return username
def get(self, request, username):
username = self.get_object(username)
serializer = UserSerializer(username)
return Response(serializer.data)
urls.py
from django.conf.urls import url
from django.urls import path
from . import views
from .views import CompanyDetailsView, UserList, UserDetail
urlpatterns = [
path('details/<int:pk>/', CompanyDetailsView.as_view(), name='company_details'),
path('users/<str:username>/', UserDetail.as_view(), name='profile_view'),
]
对 PATCH
方法使用不同的序列化程序。
class CompanyDetailsView(generics.RetrieveUpdateAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
def get_serializer_class(self):
if self.request.method == 'PATCH':
return CompanyPatchSerializer
return self.serializer_class
并在序列化程序中处理您需要的内容
class CompanyPatchSerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = '__all__'
@transaction.atomic
def update(self, instance, validated_data):
if 'monitors' in validated_data:
monitor_ids = validated_data.pop('monitors')
# todo: handle get_or_create
return super().update(instance, validated_data)
我不喜欢隐式的 m2m 模型,所以我的建议是也添加 through
模型。
我不建议重写补丁方法,但下面的代码应该可以完成这项工作
def patch(self, request, *args, **kwargs):
request.data._mutable = True
new_monitors = request.data.get('monitors')
existing_monitors = Company.objects.get(kwargs.get('id')).values_list('monitors__id', flat=True)
all_monitors = existing_monitors + new_monitors
request.data.update({'monitors': set(all_monitors)})
return self.partial_update(request, *args, **kwargs)
我是 DRF 的新手,不知道如何在不删除前一个对象的情况下将对象附加到 多对多 字段一.
我正在使用 PATCH 来更新字段 MONITORS,但是之前的值被实际值所取代。我要追加。
API GET
是:
{
"id": 1,
"created": "2018-05-02T23:43:07.605000Z",
"modified": "2021-04-03T10:25:12.280896Z",
"companies_house_id": "",
"name": "Ellison PLC",
"description": "",
"date_founded": "2018-04-28",
"country": 4,
"creator": 7,
"monitors": [
3
]
}
在 PATCH {"monitors":[11]}
之后我得到:
{
"id": 1,
"created": "2018-05-02T23:43:07.605000Z",
"modified": "2021-04-03T10:25:12.280896Z",
"companies_house_id": "",
"name": "Ellison PLC",
"description": "",
"date_founded": "2018-04-28",
"country": 4,
"creator": 7,
"monitors": [
11
]
}
我希望最终的 GET API 是 "monitors": [3, 11]
models.py
class Company(TimeStampedModel):
companies_house_id = models.CharField(max_length=8, blank=True)
name = models.CharField(max_length=200, blank=True)
description = models.TextField(blank=True)
date_founded = models.DateField(null=True, blank=True)
country = models.ForeignKey(Country, on_delete=models.PROTECT, blank=True)
creator = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
related_name='companies_created'
)
monitors = models.ManyToManyField(
settings.AUTH_USER_MODEL,
blank=True,
related_name='companies_monitored',
help_text='Users who want to be notified of updates to this company'
)
def __unicode__(self):
return u'{0}'.format(self.name)
serializers.py
from rest_framework import serializers
from .models import Company
from django.contrib.auth import get_user_model
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
companies_monitored = CompanySerializer(many=True, read_only=True)
# companies_moniotred = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = get_user_model()
fields = ('id', 'username', 'companies_monitored')
views.py
class CompanyDetailsView(generics.RetrieveUpdateAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
class UserList(generics.ListCreateAPIView):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
def get_object(self, username):
username = get_object_or_404(get_user_model(), username=username)
return username
def get(self, request, username):
username = self.get_object(username)
serializer = UserSerializer(username)
return Response(serializer.data)
urls.py
from django.conf.urls import url
from django.urls import path
from . import views
from .views import CompanyDetailsView, UserList, UserDetail
urlpatterns = [
path('details/<int:pk>/', CompanyDetailsView.as_view(), name='company_details'),
path('users/<str:username>/', UserDetail.as_view(), name='profile_view'),
]
对 PATCH
方法使用不同的序列化程序。
class CompanyDetailsView(generics.RetrieveUpdateAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
def get_serializer_class(self):
if self.request.method == 'PATCH':
return CompanyPatchSerializer
return self.serializer_class
并在序列化程序中处理您需要的内容
class CompanyPatchSerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = '__all__'
@transaction.atomic
def update(self, instance, validated_data):
if 'monitors' in validated_data:
monitor_ids = validated_data.pop('monitors')
# todo: handle get_or_create
return super().update(instance, validated_data)
我不喜欢隐式的 m2m 模型,所以我的建议是也添加 through
模型。
我不建议重写补丁方法,但下面的代码应该可以完成这项工作
def patch(self, request, *args, **kwargs):
request.data._mutable = True
new_monitors = request.data.get('monitors')
existing_monitors = Company.objects.get(kwargs.get('id')).values_list('monitors__id', flat=True)
all_monitors = existing_monitors + new_monitors
request.data.update({'monitors': set(all_monitors)})
return self.partial_update(request, *args, **kwargs)