UniqueTogetherValidator model serializer or model viewset logic for custom "lat-long" django model field with pre-save method

1,038 views
Skip to first unread message

Mark Mikofski

unread,
Mar 23, 2017, 6:47:11 PM3/23/17
to Django REST framework
Hi, I have a Django (1.8.5) model with a custom field that calls pre-save() to evaluate the field from other model fields:

class LatLongField(models.CharField):
   
"""
    Custom field class based on :class:`~django.db.models.CharField` that
    creates a string field from the site coordinates.
    """

   
def pre_save(self, model_instance, add):
       
"""
        Create the lat long field the every time the model is saved.
        """

       
# save latitude and longitude with 3 decimal places
        value
= '%.3f %.3f' % (model_instance.latitude,
                               model_instance
.longitude)
        setattr
(model_instance, self.attname, value)
       
return value

This custom field is used in a model with latitude and longitude to create a string that can be "unique". The custom field is used in a unique together constraint:

class Weather(models.Model):
    latitude
= models.FloatField()
   
longitude = models.FloatField()
    header
= models.CharField()
    datatype
= models.ChoiceField()
    latlong
= models.LatLongField()

If I make a model serializer no matter what I get, lat-long field is required. If I exclude it from the serializer, I get no validators and an integrity error exception (500 server error)

Is this a django issue or a drf issue?


Xavier Ordoquy

unread,
Mar 23, 2017, 7:29:03 PM3/23/17
to django-res...@googlegroups.com
Hi,

I’m definitively not sure about that too.
Could you try to create a Weather instance as Weather.objects.create(latitude=xx, longitude=xx) and see if it works and set correctly automatically the latlong value, then it’s likely a DRF issue.
Otherwise, you may want to discuss that on the django user mailing list first.

Regards,
Xavier Ordoquy,
Linovia.

--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-fram...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Mark Mikofski

unread,
Mar 23, 2017, 9:00:37 PM3/23/17
to Django REST framework
Hi Xavier,

Thanks for your quick reply. I had to rush off and I didn't get to finish the post. Sorry!

Yes, I can make model objects and save them to the database using w = Weather(latitude=x, longitude=y, header="blah", datatype=3) and w.save().

The issue only occurs when I test the unique together constraint by posting a duplicate.

If the serializer looks like this:

class WeatherSerializer(serializers.ModelSerializer):
   
"""weather serializer"""

    class Meta:
        model
= Weather
        exclude
= ('lat_long',)

then I can post new records fine. But if I try to post a duplicate that's when I get the integrity error, and since the validator is missing from the serializer, the server says 500.

If I remove the line `exclude = ('lat_long')` then the serializer _does_ have the `UniqueTogetherValidator` which I can see in a Django shell as WeatherSerializer().validators

However, now if I try to post a duplicate, instead of getting 500, I do get 400, good, but the message is not "non_field_errors": ["The fields lat_long, header, datatype must make a unique set."], instead I get "lat_long": ["This field is required."]

* I realize I should/could use DecimalField for latitude and longitude and that would resolve my problem.
* I tried using HiddenField but couldn't get it to make the lat_long value without knowing the serializer object a priori
* SerializerMethodField with get_lat_long(obj) method didn't work either

Xavier Ordoquy

unread,
Mar 24, 2017, 2:29:27 AM3/24/17
to django-res...@googlegroups.com
Le 24 mars 2017 à 02:00, Mark Mikofski <bwana...@gmail.com> a écrit :

Hi Xavier,

Thanks for your quick reply. I had to rush off and I didn't get to finish the post. Sorry!

Yes, I can make model objects and save them to the database using w = Weather(latitude=x, longitude=y, header="blah", datatype=3) and w.save().

There are slightly but significant differences between the .save() and Weather.objects.create(..)


The issue only occurs when I test the unique together constraint by posting a duplicate.

If the serializer looks like this:

class WeatherSerializer(serializers.ModelSerializer):
   
"""weather serializer"""

    class Meta:
        model
= Weather
        exclude
= ('lat_long',)

then I can post new records fine. But if I try to post a duplicate that's when I get the integrity error, and since the validator is missing from the serializer, the server says 500.

The traceback would help understand what we are speaking about.

If I remove the line `exclude = ('lat_long')` then the serializer _does_ have the `UniqueTogetherValidator` which I can see in a Django shell as WeatherSerializer().validators

However, now if I try to post a duplicate, instead of getting 500, I do get 400, good, but the message is not "non_field_errors": ["The fields lat_long, header, datatype must make a unique set."], instead I get "lat_long": ["This field is required."]

* I realize I should/could use DecimalField for latitude and longitude and that would resolve my problem.
* I tried using HiddenField but couldn't get it to make the lat_long value without knowing the serializer object a priori
* SerializerMethodField with get_lat_long(obj) method didn't work either

If you have a unique constraint on those fields, serializer will blow because it enforces the check before saving meaning the field isn’t ready.
Exclude the field, remove the constraint and perform the check by yourself.

Regards,
Xavier Ordoquy,
Linovia.

Mark Mikofski

unread,
Mar 24, 2017, 2:55:58 AM3/24/17
to Django REST framework
Got it. That makes sense. Thanks!

here's the traceback for the case where the field is excluded, so the validator is empty and so the integrity error exception never gets handled and the response is 500.


DRF is v3.3.3, django is v1.8.5

Mark Mikofski

unread,
Mar 24, 2017, 5:53:54 AM3/24/17
to Django REST framework
SOLVED!

I added a default class with a set_context method to grab the data from the request and combined with a HiddenField and the CreateOnlyDefault to limit it to POST only

class LatLongDefault(object):
   
"""
    Provide default ``lat_long`` field for validation on POST.
    """

   
def set_context(self, serializer_field):
        request
= serializer_field.context['request']
        LOGGER
.debug('request data\n:%r', request.data)
       
self.latitude = float(request.data['latitude'])
       
self.longitude = float(request.data['longitude'])

   
def __call__(self):
       
return '%.3f %.3f' % (self.latitude, self.longitude)

   
def __repr__(self):
       
return unicode_to_repr('%s()' % self.__class__.__name__)


class
WeatherSerializer(serializers.ModelSerializer):
   
"""weather serializer"""
    lat_long = serializers.HiddenField(
       
default=serializers.CreateOnlyDefault(LatLongDefault())
    )


    class Meta:
        model
= Weather

I copied the code from the CurrentUserDefault in GitHub.

Let me just say that DRF is really well written code, several times I have gone back to the source and it's always been illuminating!
Reply all
Reply to author
Forward
0 new messages