Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writable nested serializer error with UniqueTogetherValidator #2380

Closed
hiepthai opened this issue Jan 7, 2015 · 8 comments
Closed

Writable nested serializer error with UniqueTogetherValidator #2380

hiepthai opened this issue Jan 7, 2015 · 8 comments
Labels

Comments

@hiepthai
Copy link

hiepthai commented Jan 7, 2015

Encountered this strange error, any idea what i'm missing?
Django Version: 1.7
Python Version: 2.7.3
DRF: 3.0.2

# Model
class Pin(BaseModel):
    user = models.ForeignKey(User)
    item = models.ForeignKey(Photo)
    board = models.ForeignKey(Board)

    class Meta:
        unique_together = ('item', 'board')

# Serializer
class PinSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)
    board = BoardSerializer()

    class Meta:
        model = Pin
        fields = ('id', 'user', 'item', 'board')

    def create(self, validated_data):
        board_data = validated_data.pop('board')
        board_name = board_data.get('name', 'My favorites')
        user = validated_data.get('user', None)
        board, created = Board.objects.get_or_create(name=board_name, user=user)
        return Pin.objects.create(board=board, **validated_data)

Traceback:
File "/home/django/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response

  1.                 response = wrapped_callback(request, _callback_args, *_callback_kwargs)
    
    File "/home/django/local/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  2.     return view_func(_args, *_kwargs)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/viewsets.py" in view
  3.         return self.dispatch(request, _args, *_kwargs)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
  4.         response = self.handle_exception(exc)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
  5.         response = handler(request, _args, *_kwargs)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/mixins.py" in create
  6.     serializer.is_valid(raise_exception=True)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/serializers.py" in is_valid
  7.             self._validated_data = self.run_validation(self.initial_data)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/serializers.py" in run_validation
  8.         self.run_validators(value)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/fields.py" in run_validators
  9.             validator(value)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/validators.py" in call
  10.     queryset = self.filter_queryset(attrs, queryset)
    
    File "/home/django/local/lib/python2.7/site-packages/rest_framework/validators.py" in filter_queryset
  11.     return queryset.filter(**filter_kwargs)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/manager.py" in manager_method
  12.             return getattr(self.get_queryset(), name)(_args, *_kwargs)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/query.py" in filter
  13.     return self._filter_or_exclude(False, _args, *_kwargs)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/query.py" in _filter_or_exclude
  14.         clone.query.add_q(Q(_args, *_kwargs))
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/sql/query.py" in add_q
  15.     clause, require_inner = self._add_q(where_part, self.used_aliases)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/sql/query.py" in _add_q
  16.                 current_negated=current_negated, connector=connector)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/sql/query.py" in build_filter
  17.                                                 lookups, value)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/fields/related.py" in get_lookup_constraint
  18.                 lookup_class(Col(alias, target, source), val), AND)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/lookups.py" in init
  19.     self.rhs = self.get_prep_lookup()
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/lookups.py" in get_prep_lookup
  20.     return self.lhs.output_field.get_prep_lookup(self.lookup_name, self.rhs)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/fields/init.py" in get_prep_lookup
  21.         return self.get_prep_value(value)
    
    File "/home/django/local/lib/python2.7/site-packages/django/db/models/fields/init.py" in get_prep_value
  22.     return int(value)
    

Exception Type: TypeError at /api/v1/pin/
Exception Value: int() argument must be a string or a number, not 'OrderedDict'

@tomchristie
Copy link
Member

The model serializer here is generating a UniqueTogetherValidator for item and board, although because board is a nested object it's passing the dictionary of data for it rather than a primary key.

We'll want to look into handling this more gracefully and documenting some of the constraints that unique together validators cannot automatically be applied too.

In the immediate case you'll probably want to turn of the default validators by instead specifying...

class Meta:
    validators = []

Then add the validation in explicitly.

def validate(self, attrs):
    # Ensure that a `Pin` with board_name=attrs['board']['name'] and item=attrs['item'] does not already exist.

You probably want to stop using ModelSerializer and just use a fully explicit serializer at this point too, since you're already overriding most of the fields, and the create method.

@tomchristie
Copy link
Member

Also an off-topic point...

user = validated_data.get('user', None)

You've put read_only=True on the user field, so it will always be None.

@xordoquy
Copy link
Collaborator

xordoquy commented Jun 1, 2015

We're missing a feedback on this. I'm going to close it.
Moreover, I'm going to assume #2975 fixes the unique together issue would there still be one.
Feel free to reopen if you have more about this.

@xordoquy xordoquy closed this as completed Jun 1, 2015
@bjarnoldus
Copy link

I think this bug is still open. At least in 3.2.4 i got the same error:

self <UniqueTogetherValidator(queryset=Foo.objects.all(), fields=('foo', 'ip'))>
queryset Error in formatting: DataError: invalid input syntax for type inet: "OrderedDict([('address', '5.3.2.5')])" LINE 1: ...foo" WHERE ("foo"."ip_id" = 'OrderedDi...

attrs {'foo': <Foo: foo>, 'ip': OrderedDict([('address', '5.3.2.5')])}

@xordoquy
Copy link
Collaborator

the OP issue is to my opinion linked to opposite constraints (write unique together with a read only field).

@bjarnoldus
Copy link

I don't know if the 'read only' field changes the behavior. I've a unique constraint on two foreign keys, where the second one is an IP address. I needed to add the 'validators=[]" statement to Meta to turn off the validators. In some way the validator cannot handle complex unique_together constraints. For me it is solved using the proposed workaround, but maybe the unique_together in the library should be extended to handle these kind of fields.

@xordoquy
Copy link
Collaborator

This sounds like a different issue. Would help if you could come with a failing test case

@cderwin
Copy link

cderwin commented Feb 12, 2016

I'm still experiencing this issue (Django 1.9, DRF 3.3, Python 2.7). I don't have a test case at the moment, but I'll try to post one over the long weekend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants