1

I'm using Django and django-restfframework for following task. I need these 3 models. (1) Person model to keep customer info. (2) Sensor model and (3) DataPoint model. Each is a one to many relationship. A Person can have many sensors and each sensor can have many data points but never the other way around. I have created following models in my models.py file. ..

class Person(models.Model):
    first_name = models.CharField(max_length=30, null=False)
    last_name = models.CharField(max_length=30, null=False)

class Sensor(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    name = models.CharField(max_length=100, null=False)
    sensor_id = models.UUIDField(null=True, blank=True)

class DataPoint(models.Model):
    sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE)
    date_time_instance = models.DateTimeField()
    value = models.DecimalField(max_digits=10, decimal_places=4)

Serializer.py has following code...

class PersonSerializer(serializers.ModelSerializer):

    class Meta:
        model = Person
        fields = ('id', 'first_name', 'last_name')


class SensorSerializer(serializers.ModelSerializer):

    person = PersonSerializer()
    class Meta:
        model = Sensor
        fileds = ('id', 'name', 'person')

class DataPointSerializer(serializers.ModelSerializer):

    sensor = SensorSerializer()
    class Meta:
        model = DataPoint
        fileds = ('id', 'date_time_instance', 'value', 'sensor')

views.py has following code for datapoint API request...

@api_view(['GET', 'POST'])
def datapoint_list(request):
    if request.method == 'GET':
        dp = DataPoint.objects.all()
        serializer = DataPointSerializer(dp, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = DataPointSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Here are my questions:-

  1. Have I created good relation database schema?

  2. It seems when I do a datapoint POST request, I have to give all Person and all sensor information. How can I just supply the datapoint request (time stamp and data value) and let everything else be taken care of (i.e. proper sensor and person check) ?

  3. Would a non relation database scheme help ? How ?

  4. Where can I specify that all requests are JSON requests in my django app ?

Thanks in advance for your help.

1
  • Could you clarify #2 a bit? I think I see what you mean, however a POST request to the datapoint API endpoint would be creating an entirely new datapoint, so the Person and Sensor couldn't be known unless you specified them. If you want to update a datapoint, you could accept PUT and PATCH methods. If you're set on using only POST, you could write your save() method in the DataPointSerializer such that it parsed the Person and Sensor from the URL. Ex: POST to `/api/datapoint/<Person_id>/<Sensor_id>/' and then pull kwargs from the URL when creating (or updating, if exists) the datapoint. Commented Feb 25, 2016 at 13:11

1 Answer 1

1

After thinking about your question a little more, I think I see what you're trying to do. It doesn't make sense to update a datapoint (I'm guessing) so POST would really be the only creation method of interest.

The serializer create() method is called when instantiating the serializer so if you override that, you can specify the Sensor however you'd like, and it'll be taken care of in the save(). You just have to make sure you pass in the Sensor when creating the serializer- otherwise I don't think there's any way to properly set the relationship.

Here's how I'd go about it, assuming the Sensor is specified in the endpoint URL. This assumes the Sensor already exists, but to make it more robust you could use get_or_create()- though including an unused Sensor id in the POST would likely be difficult/weird, it'd make more sense to use a different endpoint to create a new Sensor first :

views.py:

@api_view(['GET', 'POST'])
def datapoint_list(self, request):
  if request.method == 'GET':
    dp = DataPoint.objects.all()
    serializer = DataPointSerializer(dp, many=True)
    return Response(serializer.data)

  elif request.method == 'POST':
    serializer = DataPointSerializer(data=request.data, context = {'sensor': self.kwargs['sensor'],})
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

serializers.py:

class DataPointSerializer(serializers.ModelSerializer):

    sensor = SensorSerializer()
    class Meta:
        model = DataPoint
        fileds = ('id', 'date_time_instance', 'value', 'sensor')

    def create(self, validated_data):
      sensor = Sensor.objects.get(id=self.context.get('sensor'))
      datapoint = DataPoint.objects.create(sensor=sensor)
      value = validated_data.pop('value')
      datapoint.value = value
      datapoint.save()
      return datapoint

Then if you POSTed the value to something like /api/<sensor_id>/, that should work. Hopefully someone will correct me if I'm wrong.

I hope this helps- it seems like you were passing the Sensor and Person in the POST before, and this should be a way around that. If it's not what you were looking for, just edit your question with more details.

A couple notes:

If the Person is known, that could be part of the URL as well. If the Person is the requesting user, that criteria could just be pulled out of request and used in the create() method- just pass the request in the serializer context.

Currently your GET branch returns all DataPoints, but if you used the URL I mentioned, you could filter on the Sensor id in the URL. Or maybe you want all of them, it's hard to tell what you're aiming for without more context.

If you want it to accept more than one value on POST, you'll need to add many=True on the serializer instantiation in the POST branch

EDIT: I was so focused on #2 that I forgot your other points.

  1. I think your schema is fine- like i said, it's hard to evaluate without any other context, but from what you've said about how DataPoints, Sensors, and Persons relate, it seems like the ideal configuration.

  2. I don't know enough about non relational DBs to say, but I can't see why it would be better in this case.

  3. I don't know that you can set a default request format, though it's certainly possible. If you append '/?format=json' to your request at the API endpoint, DRF interprets it correctly and returns only JSON.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks a bunch for your answer @dkhaupt, you got my question right, I wanted to know if there is a way not to give the entire person and sensor object when I do a post request to enter a new datapoint. By your solution, it does seem that I at least have to give person ID and sensor ID (not the whole objects) with every single datapoint JSON POST request, there is no way around it.
I have previously created Person and Sensor by their own POST requests so there are no unknowns. So just to reiterate, my JSON REST POST request has header as 'Content-Type: application/json' and my datapoint JSON POST payload should have this, right ? { "sensor_id": 1, "person_id": 1, "value": 1.15, }
I don't believe you'd need the 'person_id'- if the Sensor id is known, that should be enough since datapoints do not have a foreign key to Person. What happens when you leave out the 'person_id' from the POST?
@LuckyStarr did you try leaving out person_id from the POST?
@LuckyStarr if this answer helped you, you can accept it so that others with this question know it helped in your situation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.