I am relatively new to DRF, and I'm trying to figure out this issue which appears to be slightly different than all the documentation I can find. In my toy example, let's say I have hundreds of thousands of records of people in a model, and another model with all their cars owned and another with all their other property owned:
class Person(models.Model):
first_name = models.CharField()
last_name = models.CharField()
zipcode = models.IntegerField()
favorite_color = models.CharField()
class Meta:
unique_together = ('first_name', 'last_name', 'zipcode', 'favorite_color')
class Car(models.Model):
person = models.ForeignKey(Person)
carmake = models.CharField()
carmodel = models.CharField()
VIN = models.CharField()
class OtherProperty(models.Model):
person = models.ForeignKey(Person)
prop_type = models.CharField()
prop_value = models.Integer()
Let's say I have all the people already loaded in the database. Now I have a big batch of new records of all the new car purchases by ten thousand people. Some people bought multiple cars, and some people bought no cars. I want to make a highly capable API and keep things simple for the client program, so I want to directly upload (via a POST call) all the new car purchases. The trick is that the client program doesn't have the unique primary keys of each Person record. So, I want the client to submit a json POST something like this:
[ {'person' : {'first_name':'Joe',
'last_name': 'Smith',
'zipcode': 80110,
'favorite_color': 'Blue'},
'carmake' : 'Subaru',
'carmodel' : 'Outback',
'VIN' : 12345123123},
{'person' : {'first_name':'Amy',
'last_name': 'Adams',
'zipcode': 92075,
'favorite_color': 'White'},
'carmake' : 'BMW',
'carmodel' : '325ic',
'VIN' : 987165334},
.... 10,000 more records... ]
Right now, I have a solution in which I use views.py to read the request.data, validate that the Person fields are there with validated content, write a QuerySet to the Person model to load all of the Person primary keys into a dict, and return an error if not all of the Person instances already exist. Then I make a new dict based on the request.data, but with all the person sub-dicts replaced with the newly found primary keys. Then I call the CarSerializer which has a Meta field for a list_serializer_class that uses the django function bulk_create (as per the documentation: http://www.django-rest-framework.org/api-guide/serializers/#customizing-multiple-create). This works.
But, I think that Person lookup code does not belong in the Car ViewSet, right? And, I am probably duplicating a lot of what django/DRF already does with validation. Furthermore, now I need to copy and paste that code to the OtherProperty ViewSet to do the same Person primary key lookup. In actuality, I have even more models which have a ForeignKey to the Person model and I know I shouldn't copy/paste all that primary key lookup and validation code.
So, my question is, where can I put that Person primary key lookup code? It kind of seems like something that a serializer should do, but I'm not trying to save any new Person instances. And, because of the large number of "Cars" (or "OtherProperty") instances I am creating, I need that to be a ListSerializer (bulk_create) call, so I don't think I can just rely on the nested relationship and expect it to work. Am I missing an obvious solution?
Thanks for any suggestions.
edit
I had a couple ideas as potential solutions:
create a PersonLookup serializer which is NOT attached to the Person model. This serializer would have the Person fields, take in the full request.data and use the serializer functionality to do validation, but then instead of creating any records, it would return the original request.data with PKs instead of the detailed Person info so that the returned data can be handled by the ViewSet and sent to the Car or OtherProperty Serializers.
Add a unique field to the Person model which is an underscore separated concatenation of all the non-unique fields. Then the client will be able to create those fields via concatenation and the request.data won't be nested. Actually, this might not work since bulk_create is going to need the primary keys, not some unique field, I think (?). Another problem with this is then I'll be storing all the Person fields twice, once in the unique name and another time in the individual fields. Maybe there is a Manager trick to fix that problem.