Reputation: 665
My problem is that when trying to run is_valid() on a big chunk of data in a POST-request, where the model has a foreign key, it will fetch the foreign key table for each incoming item it needs to validate.
This thread describes this as well but ended up finding no answer: Django REST Framework Serialization POST is slow
This is what the debug toolbar shows:
My question is therefore, is there any way to run some kind of select_related on the validation? I've tried turning off validation but the toolbar still tells me that queries are being made.
These are my models:
class ActiveApartment(models.Model):
adress = models.CharField(default="", max_length=2000, primary_key=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
class Company(models.Model):
name = models.CharField(blank=True, null=True, max_length=150)
These are my serializers: I have tried not using the explicit PrimaryKeyRelatedField as well, having validators as [] doesn't seem to stop the validation either for some reason.
class ActiveApartmentSerializer(serializers.ModelSerializer):
company = serializers.PrimaryKeyRelatedField(queryset=Company.objects.all())
class Meta:
model = ActiveApartment
list_serializer_class = ActiveApartmentListSerializer
fields = '__all__'
extra_kwargs = {
'company': {'validators': []},
}
class ActiveApartmentListSerializer(serializers.ListSerializer):
def create(self, validated_data):
data = [ActiveApartment(**item) for item in validated_data]
# Ignore conflcits is the only "original" part of this create method
return ActiveApartment.objects.bulk_create(data, ignore_conflicts=True)
def update(self, instance, validated_data):
pass
This is my view:
def post(self, request, format=None):
# Example of incoming data in request.data
dummydata = [{"company": 12, "adress": "randomdata1"}, {"company": 12, "adress": "randomdata2"}]
serializer = ActiveApartmentSerializer(data=request.data, many=True)
# This will run a query to check the validity of their foreign keys for each item in dummydata
if new_apartment_serializer.is_valid():
print("valid")
Any help would be appreciated (I would prefer not to use viewsets)
Upvotes: 2
Views: 881
Reputation: 665
The way I solved it was I took the direction athanasp pointed me in and tweaked it a bit as his solution was close but not working entirely.
I:
class CitySerializer(serializers.ModelSerializer): class Meta: model = City fields = ('name',)
class ListingSerializer(serializers.ModelSerializer):
# city = serializers.IntegerField(required=True, source="city_id") other alternative
city = CitySerializer()
def __init__(self, *args, **kwargs):
self.cities = City.objects.all()
super().__init__(*args, **kwargs)
def validate_city(self, value):
try:
city = next(item for item in self.cities if item.name == value['name'])
except StopIteration:
raise ValidationError('No city')
return city
And this is how the data looks like that should be added:
city":{"name":"stockhsdolm"}
Note that this method more or less works using the IntegerField() suggested by athan as well, the difference is I wanted to use a string when I post the item and not the primary key (e.g 1) which is used by default. By using something like city = serializers.IntegerField(required=True, source="city_id") works as well, just tweak the validate method, e.g fetching all the Id's from the model using values_list('id', flat=True) and then simply checking that list for each item.
Upvotes: 0
Reputation: 233
Have you tried to define company as IntegerField in the serializer, pass in the view's context the company IDs and add a validation method in field level?
class ActiveApartmentSerializer(serializers.ModelSerializer):
company = serializers.IntegerField(required=True)
...
def __init__(self, *args, **kwargs):
self.company_ids = kwargs.pop('company_ids', None)
super().__init__(*args, **kwargs)
def validate_company(self, company):
if company not in self.company_ids:
raise serializers.ValidationError('...')
return company
Upvotes: 1