I have a DRF API with a model of months, I need to provide a way to make one endpoint for creating or updating objects (so either post or patch endpoint).
I tried taking example from these two answers:
overriding view's create method for update or create
The second answer is using the view and not the serializer and that's a behaviour I would rather keep.
My code so far:
class MultipleFieldLookupMixin:
def get_object(self):
queryset = self.get_queryset()
queryset = self.filter_queryset(queryset)
filter = {}
for field in self.lookup_fields:
if self.kwargs.get(field, None):
filter['field'] = self.kwargs['field']
obj = get_object_or_404(queryset, **filter)
self.check_object_permissions(self.request, obj)
return obj
class MonthsViewSet(ModelViewSet, MultipleFieldLookupMixin):
authentication_classes = (TokenAuthentication,)
def get_queryset(self):
query_set = Month.objects.all()
return query_set
serializer_class = MonthSerializer
lookup_fields = ['month', 'year', 'farm']
def create(self, request, *args, **kwargs):
kwarg_field: str = self.lookup_url_kwarg or self.lookup_fields
self.kwargs[kwarg_field] = request.data[self.lookup_fields]
try:
return self.update(request, *args, **kwargs)
except Month.DoesNotExist:
return super().create(request, *args, **kwargs)
serializer:
class MonthSerializer(serializers.ModelSerializer):
class Meta:
model = Month
fields = '__all__'
So now trying to create or update a field raises the following error:
self.kwargs[kwarg_field] = request.data[self.lookup_fields]
TypeError: unhashable type: 'list'
EDIT I've changed the create method to this:
def create(self, request, *args, **kwargs):
request_month = request.data['month']
year = request.data['year']
farm = request.data['farm']
days = request.data['days']
month, created = Month.objects.update_or_create(month=request_month, year=year, farm_id=farm,
defaults={'month': request_month, 'year': year,
'farm_id': farm, 'days': days})
if not created:
serializer = MonthSerializer(month)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=HTTP_200_OK)
elif not month:
serializer = MonthSerializer(created)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=HTTP_200_OK)
My model has a unique toghther constraint:
class Month(models.Model):
month = models.IntegerField(null=False)
year = models.IntegerField(null=False)
date = models.DateField(null=True, blank=True, default=None)
days = models.DurationField(default=0, )
farm = models.ForeignKey(to=Farm, on_delete=models.CASCADE, related_name='months')
objects = models.Manager()
def save(self, *args, **kwargs):
self.date = datetime.date(self.year, self.month, 1)
super(Month, self).save(*args, **kwargs)
def __str__(self):
return f'{self.month},{self.year}'
class Meta:
unique_together = ('farm', 'date')
but for some reason, it's not updating the object but tries to create it which then raises a constraint violation. Which raises this error now:
tegrityError at /farm_api/months/
duplicate key value violates unique constraint "farm_api_month_farm_id_date_9d276785_uniq"
DETAIL: Key (farm_id, date)=(1, 2020-08-01) already exists.