简体   繁体   中英

Django Rest Framework: How to ignore 'unique' primary key constraint when validating a serializer?

I am attempting to store large amounts of transit data in a PostgreSQL database, where a client uploads multiple records at a time (in the tens of thousands). I only want one arrival time per stop, per route, per trip and the unique identifier for that stop, route, and trip is the primary key for my table (and a foreign key in a different table). I am trying use Django's update_or_create in my serializer to either create the entry for that arrival time or update the arrival time with the latest data if it already exists. Unfortunately, while calling is_valid() on my serializer, it identifies that the repeat records violate the uniqueness constraint of the primary keys (which makes sense), giving me this error message:

'real_id': [ErrorDetail(string='actual with this real id already exists.', code='unique')]

I want to override this behavior since if the primary key isn't unique it will just update the entry. I have already tried looping over and removing validators like so:

    def run_validators(self, value):
        for validator in self.validators:
            self.validators.remove(validator)
        super(ActualSerializer, self).run_validators(value)

I have also tried removing all validators using the extra_kwargs field of my serializer Meta class like so:

extra_kwargs = {
            'name': {'validators': []}
        }

I don't really want to do the insertion in my view since ideally the serializer is parsing out the values and validating the other constraints.

But none of these solutions have changed anything. I can't find anything else on SO that would address my problem (but if someone finds an answer I missed, that would be a godsend). I think my question is somewhat similar to this question but that one doesn't have a good answer.

For reference, my serializers file:

from .models import Future, Actual
from rest_framework import serializers


class ActualSerializer(serializers.ModelSerializer):

    def create(self, validated_data):
        actual, created = Actual.objects.update_or_create(
            real_id=validated_data['real_id'],
            defaults={'arrival': validated_data.get('arrival', None)}
        )
        return actual

    class Meta:
        model = Actual
        fields = ['real_id', 'arrival']


class FutureSerializer(serializers.ModelSerializer):

    class Meta:
        model = Future
        fields = ['row_id', 'stop_id', 'route_id', 'record_time', 'predicted_arrival', 'delay', 'real_id']

My models:

from django.db import models
import json


class Actual(models.Model):
    real_id = models.CharField(max_length=100, primary_key=True)
    arrival = models.BigIntegerField(null=True)

    def __str__(self):
        return json.dumps([self.real_id, self.arrival])


class Future(models.Model):
    row_id = models.BigAutoField(primary_key=True)
    stop_id = models.CharField(max_length=20)
    route_id = models.CharField(max_length=10)
    record_time = models.BigIntegerField()
    predicted_arrival = models.IntegerField(null=True)
    delay = models.IntegerField()
    real_id = models.ForeignKey(Actual, on_delete=models.CASCADE)

    def __str__(self):
        return json.dumps([self.row_id,
                           self.stop_id,
                           self.route_id,
                           self.record_time,
                           self.predicted_arrival,
                           self.delay,
                           self.real_id])

And finally, my view:

class ArrivalData(APIView):

    queryset = Future.objects.all()
    permission_classes = [OperationAllowed]

    def post(self, request: Request, format=None) -> Response:
        parsed_data = json.loads(request.data['data'])
        actual_arrivals = []
        for datum in parsed_data:
            saved_time = None
            if datum['predicted_arrival'] is not None and -200 < datum['predicted_arrival'] - datum['record_time'] < 0:
                saved_time = datum['predicted_arrival']

            actual_arrivals.append({
                'real_id': datum['real_id'],
                'arrival': saved_time,
            })

        actual_serializer = ActualSerializer(data=actual_arrivals, many=True)
        if actual_serializer.is_valid(raise_exception=False):
            actual_serializer.save()
        future_serializer = FutureSerializer(data=parsed_data, many=True)

        if future_serializer.is_valid():

            future_serializer.save()
            return Response("Data saved")
        
        return Response(str(actual_serializer.errors))

So, turns out the unique constraint is a field-level validator which is why trying to remove it at the class level wasn't working.

I explicitly declared the field in the serializer class without any validators real_id = serializers.CharField(validators=[]) , which fixed the problem.

This page ultimately helped me if anyone else has this issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM