LuckyStarr
LuckyStarr

Reputation: 1555

Django RDBMS model design for rest framework

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.

Upvotes: 1

Views: 285

Answers (1)

dkhaupt
dkhaupt

Reputation: 2348

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.

Upvotes: 1

Related Questions