dhango class based views - Saving post data

141 views
Skip to first unread message

Shekar Tippur

unread,
Jun 2, 2015, 3:47:12 AM6/2/15
to django...@googlegroups.com
Hello,

I am trying to save post data and I get a error. I am trying to use enum field.

prefs' is an invalid keyword argument for this function
Request Method:POST
Request URL:http://127.0.0.1:8000/setPrefs/
Django Version:1.8.2
Exception Type:TypeError

Here is my model

class UserPrefs(models.Model):

Preferences = (
('0','Likes'),
('1','Dislikes'),
('2','Shared'),
('3','Rejected'),
)
user = models.ForeignKey(AuthUser, related_name='Screens')
#user = models.ForeignKey(AuthUser)
address = models.ForeignKey(Address)
prefs = models.CharField(max_length=1,choices=Preferences)
class Meta:
#managed = False
db_table = 'user_pref'

def save(self, *args, **kwargs):
super(Screens, self).save(*args, **kwargs)

Appreciate if someone can shed some light on this.

- Shekar

James Schneider

unread,
Jun 2, 2015, 10:54:14 AM6/2/15
to django...@googlegroups.com

Can you post the entire traceback for the error, and the view code as well?

-James

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/206ffba0-c578-4bbd-8749-7d174d42cc93%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Shekar Tippur

unread,
Jun 2, 2015, 11:39:24 AM6/2/15
to django...@googlegroups.com
Here is the trace:

Traceback:

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response

  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/django/views/decorators/csrf.py" in wrapped_view

  58.         return view_func(*args, **kwargs)

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/django/views/generic/base.py" in view

  71.             return self.dispatch(request, *args, **kwargs)

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/rest_framework/views.py" in dispatch

  451.             response = self.handle_exception(exc)

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/rest_framework/views.py" in dispatch

  448.             response = handler(request, *args, **kwargs)

File "/Users//PycharmProjects///views.py" in post

  88.             serializer.save()

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/rest_framework/serializers.py" in save

  165.             self.instance = self.create(validated_data)

File "/Users//PycharmProjects///modelserializer.py" in create 

  48.         return Screens.objects.create(**validated_data)

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/django/db/models/manager.py" in manager_method

  127.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/django/db/models/query.py" in create

  346.         obj = self.model(**kwargs)

File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/django/db/models/base.py" in __init__

  480.                 raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])


Exception Type: TypeError at //setPrefs/

Exception Value: 'prefs' is an invalid keyword argument for this function

and View:

class AddToUserProfile(generics.CreateAPIView):

    permission_classes = (permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly)

    serializer_class = UserPrefSerializer

    queryset = UserPrefs.objects.all()

    lookup_field = 'user_id'

    #def create(self,request,*args, **kwargs):

    def post(self, request, *args, **kwargs):

        serializer = UserPrefSerializer(data=request.data)

        print (repr(serializer))

        user=request.user

        if serializer.is_valid():

            serializer.save()

            return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:

            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Shekar Tippur

unread,
Jun 2, 2015, 2:31:24 PM6/2/15
to django...@googlegroups.com
Just to add, if I use a curl request 

curl -H  "Authorization: Bearer $usertoken" -H "Content-Type: application/json" -X POST -d '{"user":"foo1","stock":"XYZ","prefs":"Likes"}' http://${endpoint}/addPrefs

I get a error: {"prefs":["\"Likes\" is not a valid choice."]}

If I use

curl -H  "Authorization: Bearer $usertoken" -H "Content-Type: application/json" -X POST -d '{"user":"foo1","stock":"XYZ","prefs":"1"}' http://${endpoint}/addPrefs

I get the above trace. 

James Schneider

unread,
Jun 2, 2015, 3:33:39 PM6/2/15
to django...@googlegroups.com

I just looked over your model again. Your save() override has a super(Screens,...) reference, which doesn't match the model class.

That may explain why you are getting the invalid parameters error, since you are probably calling the wrong save function from a different class.

I'd remove that save() override entirely.

-James

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.

Shekar Tippur

unread,
Jun 2, 2015, 4:12:54 PM6/2/15
to django...@googlegroups.com
James,

I have commented save in model code. I have also changed the prefs field to be char(20) for now.

I still get 
'prefs' is an invalid keyword argument for this function
- Shekar


James Schneider

unread,
Jun 2, 2015, 5:12:35 PM6/2/15
to django...@googlegroups.com

Wait, what does your serializer look like? I found this in the traceback:

ile "/Users//PycharmProjects///modelserializer.py" in create 

  48.         return Screens.objects.create(**validated_data)

Are you sure that you are referencing the right serializer and/or is the serializer referencing the right model? I wouldn't assume that a create() call would be made for Screens.

-James

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.

Shekar Tippur

unread,
Jun 2, 2015, 7:33:44 PM6/2/15
to django...@googlegroups.com
Here is my serializer

class UserPrefSerializer(serializers.ModelSerializer):
#user = serializers.ReadOnlyField(source='owner.username')
def create(self, validated_data):
print ("Validated data")
print (validated_data)
#return Screens.objects.create(**validated_data)
return Screens.objects.create(**validated_data)

Shekar Tippur

unread,
Jun 2, 2015, 8:18:32 PM6/2/15
to django...@googlegroups.com
James,
You are right. I am able to get past that issue.
The next stumbling block is with 

null value in column "address_id" violates not-null constraint
DETAIL:  Failing row contains (10, Likes, null, null).

Here is my curl

curl -H  "Authorization: Bearer $usertoken" -H "Content-Type: application/json" -X POST -d '{"address":"XYZ","prefs":"Likes"}' http://${endpoint}/addPrefs


How do I pass the address name so that it gets translated to an id in the background.


- Shekar

Shekar Tippur

unread,
Jun 2, 2015, 8:39:14 PM6/2/15
to django...@googlegroups.com
Here is my view. 

request.data has the 2 fields I am passing however serializer.validated_data has only prefs.


class AddToUserProfile(generics.CreateAPIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly)
serializer_class = UserPrefSerializer
queryset = UserPrefs.objects.all()
lookup_field = 'user_id'

#def create(self,request,*args, **kwargs):
def post(self, request, *args, **kwargs):
serializer = UserPrefSerializer(data=request.data)
print (repr(serializer))
        #user=request.user
if serializer.is_valid():
print ("validated_data")
print (serializer.validated_data) ## Here i get only prefs - ItemsView(OrderedDict([('prefs', 'Likes')]))
print (request.data) ## Here I see both address as well as prefs {'address': 'XYZ', 'prefs': 'Likes'}
serializer.create(serializer.validated_data)

James Schneider

unread,
Jun 2, 2015, 8:41:38 PM6/2/15
to django...@googlegroups.com
The 'address' field is an FK to the Address model (hence the reference to 'address_id'), and you'll either need to 1) add null=True to the address field definition in UserPrefs and update your migrations and allow a UserPref model to not be connected to an Address model, 2) find the right Address model within your various create/save() method calls and include it when creating a UserPref object, or 3) Create a new Address object, save it, and then pass it in when creating the UserPref object.

Sounds like option 3 is the way you want to go since you are including a string that can be used to create an Address object. That would probably be best in your create() call so that a new Address object isn't created whenever you update your UserPref object.

Just keep in mind that you are creating two objects in this view, one UserPref, and an Address that gets attached to your UserPref. Simply setting userpref.address to "XYZ" is incorrect.

-James



--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.

Shekar Tippur

unread,
Jun 2, 2015, 8:50:52 PM6/2/15
to django...@googlegroups.com
James.

address table already has a record of name xyz. Request object has a address value of address: xyz.

As I am initializing serialize with request.data, why is it that I don't see it in validated_data?
Do I need to add address_id to validated_data in my view?

James Schneider

unread,
Jun 2, 2015, 11:13:17 PM6/2/15
to django...@googlegroups.com

No, it looks like you need to set a model in the Meta class as part of your ModelSerializer class.

http://www.django-rest-framework.org/api-guide/serializers/#modelserializer

Is "XYZ" a valid slug to retrieve that Address object? You probably need to provide the PK value for the 'address' rather than some other field, otherwise you'll be doing some crazy overriding to pull the right Address object.

Your submitted data dictionary is probably going to look something like this:

{"address":"14","prefs":"0"}'

Where 14 is the PK of the Address object you want to use.

I'm a little out in the weeds here since I don't have a DRF setup of my own, so please make sure you read through the docs.

Typically, you won't be submitting the "human readable" version of related (foreign key) field via POST, it will almost always be a PK. The same rule applies for any field that has a 'choices' attribute, you would submit the key value, not the display value. In the case of 'prefs', this would be "1", "3", etc. rather than "Likes" or "Dislikes", per your Preferences list.

API's are made for computers to communicate with other computers, not for people.

-James



--


You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.

To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/6d31cc5b-41d2-49b4-a193-5743330914f9%40googlegroups.com.

Shekar Tippur

unread,
Jun 3, 2015, 1:58:02 AM6/3/15
to django...@googlegroups.com
James,

I was able to get thro with the save operation. It was quite a bit of learning.

Meta section on the model existed but I was not populating validated_data properly. I was under the assumption that 

when I did

serializer=UserPrefSerializer(data=request.data)

seriazer object will be populated with post request data.

I had to resort to

             address_obj=Stock.objects.get(symbol=request.data['address'])

             user_obj=User.objects.get(username=request.user)


            serializer.validated_data['stock_id']=address_obj.id

            serializer.validated_data['user_id']=user_obj.id

I am not sure if this is the optimal way to do it but this seem to have done the trick.


Heartfelt thanks for handholding me through this.

- Shekar

James Schneider

unread,
Jun 3, 2015, 4:43:51 AM6/3/15
to django...@googlegroups.com
Whoa...where'd Stock come from? :-D

First off, the line where you create user_obj isn't needed. The user object in request.user is already the populated User object. You can either use request.user directly, or you could do user_obj = request.user.

Secondly, I don't think this is working the way you think it is. Yes, it successfully saved, but now I believe you have incorrect data.

Your 'address_obj' is actually a Stock object, and not an Address object. Your UserPref model doesn't show any relation to Stock unless you don't have the full model shown.

What you're doing is grabbing a Stock object, with a symbol=request.data['address']. You're then assigning the PK of the Stock object as if it were an Address object. That is almost certain to be a different PK than the Address object you intended. If you looked back at that UserPrefs object now, I'm guessing that it will display the wrong address.

I believe the reason that you aren't seeing the necessary data is because it isn't validating properly. Data that doesn't validate properly won't show in validated_data (at least I'm assuming so, since it should work the same way as ModelForms and cleaned_data). Using request.data instead of serializer.validated_data means that you are using the raw values that were POSTed, and haven't been checked (and...well, validated) in any way. This is a bad idea.


Honestly, there shouldn't be a reason for any of this code. I'm not sure how Stock fits in to any of this, I get the feeling I've only seen a portion of the relevant code, so my context here may be a bit skewed. 

I actually broke down and made a quick version of your model/serializer/view using DRF to make sure I was leading you in the correct direction. Here is a complete copy of the code I wrote (some minor modifications):


class Address(models.Model):
    # no idea what this model looked like, just made something simple
    name = models.CharField(max_length=250)

class UserPrefs(models.Model):

    Preferences = (
        ('0','Likes'),
        ('1','Dislikes'),
        ('2','Shared'),
        ('3','Rejected'),
    )
    user = models.ForeignKey(User)
    address = models.ForeignKey(Address)
    prefs = models.CharField(max_length=1,choices=Preferences)




class UserPrefSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserPrefs
        fields = ('id', 'address', 'prefs')




class AddToUserProfile(generics.CreateAPIView):
    serializer_class = UserPrefSerializer
    queryset = UserPrefs.objects.all()

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)




urlpatterns = [
    url(r'^addPrefs/', AddToUserProfile.as_view())
]   



After spinning all of this up and running the migrations, I created a couple of Address objects manually in the DB:

>>> from myapp.models import Address
>>> Address.objects.create(name='My Address 1')
>>> Address.objects.create(name='My Address 2')
>>> quit()

I assumed that the two addresses I just created had PK's of 1 and 2 respectively.

And I was able to successfully create UserPrefs objects with this command:

curl -H "Content-Type: application/json" -X POST -d '{"address":"2","prefs":"0"}' http://username:pass...@127.0.0.1:8000/addPrefs/

Easier than I remember from the last time I dealt with DRF a number of years ago.

Validation works out of the box as well:

curl -H "Content-Type: application/json" -X POST -d '{"address":"43","prefs":"0"}' http://username:pass...@127.0.0.1:8000/addPrefs/
{"address":["Invalid pk \"43\" - object does not exist."]}

curl -H "Content-Type: application/json" -X POST -d '{"address":"2","prefs":"25"}' http://username:pass...@127.0.0.1:8000/addPrefs/
{"prefs":["\"25\" is not a valid choice."]}

HTH,

-James


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.

Shekar Tippur

unread,
Jun 3, 2015, 11:43:23 AM6/3/15
to django...@googlegroups.com
James,

My apologies. stock_id came in as a artefact of a bad copy/paste.
The only deviation from what you have said is that the api call comes with a address string and not a id. But your solution works perfectly well as well.
Here are the right ones:

serializer=UserPrefSerializer(data=request.data)

seriazer object will be populated with post request data.
address_obj=Address.objects.get(symbol=request.data['address'])
user_obj=User.objects.get(username=request.user)

serializer.validated_data['address_id']=address_obj.id
serializer.validated_data['user_id']=user_obj.id

Thanks again,
S

James Schneider

unread,
Jun 3, 2015, 2:13:41 PM6/3/15
to django...@googlegroups.com
So you don't have any control over the API call? Ok then, you can update your serializer to take a string representing a symbol and grab the correct address object, or throw an error if it doesn't exist:


class UserPrefSerializer(serializers.ModelSerializer):

    def validate_address(self, value):
        try:
            address_obj = Address.objects.get(symbol=value)
        except Address.DoesNotExist:
            raise serializers.ValidationError("Address does not exist.")

        return address_obj

    def validate_prefs(self, value):
        # this loop searches your choices list and converts "Dislikes" to 1, etc.
        for pref_item in UserPrefs.Preferences:
            if pref_item[1] == value:
                return pref_item[0]

        raise serializers.ValidationError("Invalid pref value.")

    class Meta:
        model = UserPrefs
        fields = ('id', 'address', 'prefs')


Now if you check serializer.validated_data, you should see the correct address object. As a bonus, there is also a validator for the string versions of prefs as well.

I would highly recommend you go with the solution I posted, using the modified UserPrefSerializer above. The original curl commands you posted up should work with this modified serializer, and you wouldn't need to supply the user (it gets populated automatically): 

curl -H  "Authorization: Bearer $usertoken" -H "Content-Type: application/json" -X POST -d '{"address":"XYZ","prefs":"Likes"}' http://${endpoint}/addPrefs

Putting in an invalid address or prefs value should result in a validation error.

(Full disclosure, this is untested)

-James


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.

Shekar Tippur

unread,
Jun 3, 2015, 4:45:29 PM6/3/15
to django...@googlegroups.com
Thant worked. Thanks a lot James.

- Shekar
Reply all
Reply to author
Forward
0 new messages