2

Django REST Framework is reporting an error that a value is null even though I am sending the value when I POST the data.

The error Django reports is:

django.db.utils.IntegrityError: (1048, "Column 'owner_id' cannot be null")
[04/Apr/2016 18:40:58] "POST /api/items/ HTTP/1.1" 500 226814

The Angular 2 code that POSTs to Django REST Framework API is:

    let body = JSON.stringify({ url: 'fred', item_type: 'P', owner_id: 2 });

    let headers = new Headers();
    headers.append('Content-Type', 'application/json');

    this.http.post('http://127.0.0.1:8000/api/items/',
        body, {
        headers: headers
      })
      .subscribe(
        data => {
          alert(JSON.stringify(data));
        },
        err => alert('POST ERROR: '+err.json().message),
        () => alert('POST Complete')
      );

My Django API view looks like this:

class ItemViewSet(viewsets.ModelViewSet):
    queryset = Item.objects.all().order_by('-date_added')
    serializer_class = ItemSerializer

    """
        Use the API call query params to determing what to return

        API params can be:

        ?user=<users_id>&num=<num_of_items_to_return>&from=<user_id_of_items_to_show>
    """

    def get_queryset(self):
        this_user = self.request.query_params.get('user', None)
        restrict_to_items_from_user_id = self.request.query_params.get('from', None)
        quantity = self.request.query_params.get('num', 20)

        if restrict_to_items_from_user_id is not None:
            queryset = Item.objects.filter(owner=restrict_to_items_from_user_id, active=True).order_by('-date_added')[0:int(quantity)]
        elif this_user is not None:
            queryset = Item.objects.filter(active=True, credits_left__gt=0).exclude(pk__in=Seen.objects.filter(user_id=this_user).values_list('item_id', flat=True))[0:int(quantity)]
        else:
            queryset = Item.objects.filter(active=True, credits_left__gt=0)[0:int(quantity)]

        print("User id param is %s  and quantity is %s" % (user_id,quantity))

        return queryset

The associated model is:

class Item(models.Model):

    ITEM_TYPES = (
        ('V', 'Vine'),
        ('Y', 'YouTube'),
        ('P', 'Photo'),         # Photo is stored by us on a CDN somewhere
        ('F', 'Flickr'),
        ('I', 'Instagram'),
        ('D', 'DeviantArt'),
        ('5', '500px'),
    )
    owner           = models.ForeignKey(User, on_delete=models.CASCADE)     # Id of user who owns the item
    title           = models.CharField(max_length=60, default='')           # URL of where item resides (e.g. Vine or YouTube url)
    url             = models.CharField(max_length=250, default='')          # URL of where item resides (e.g. Vine or YouTube url)
    item_type       = models.CharField(max_length=1, choices=ITEM_TYPES)    # Type of item (e.g. Vine|YoutTube|Instagram|etc.)
    keywords        = models.ManyToManyField(Keyword, related_name='keywords')
                                                                            # E.g. Art, Travel, Food, etc.
    credits_applied = models.IntegerField(default=10, help_text='Total number of credits applied to this item including any given by VeeU admin')
                                                                            # Records the total number of credits applied to the Item
    credits_left    = models.IntegerField(default=10, help_text='The number of credits still remaining to show the item')
                                                                            # Number of credits left (goes down each time item is viewed
    credits_gifted  = models.IntegerField(default=0, help_text='The number of credits this item has been gifted by other users')
                                                                            # Number of credits users have gifted to this item
    date_added      = models.DateTimeField(auto_now_add=True)               # When item was added
    liked           = models.IntegerField(default=0)                        # Number of times this item has been liked
    disliked        = models.IntegerField(default=0)                        # Number of times this item has been disliked
    active          = models.BooleanField(default=True, help_text='If you mark this item inactive please say why in the comment field. E.g. "Inapproriate content"')
                                                                            # True if item is available for showing
    comment         = models.CharField(max_length=100, blank=True)          # Comment to be applied if item is inactive to say why

    # Add defs here for model related functions

    # This to allow url to be a clickable link
    def item_url(self):
        return u'<a href="%s">%s</a>' % (self.url, self.url)
    item_url.allow_tags = True

    def __str__(self):
        return '%s: Title: %s, URL: %s' % (self.owner, self.title, self.url)

I can't see what is wrong with my POST call, or the Django code.

EDIT: Added serializer code Here is the associated serializer

class ItemSerializer(serializers.HyperlinkedModelSerializer):
    username = serializers.SerializerMethodField()

    def get_username(self, obj):
        value = str(obj.owner)
        return value

    def get_keywords(self, obj):
        value = str(obj.keywords)
        return value

    class Meta:
        model = Item
        fields = ('url', 'item_type', 'title', 'credits_applied', 'credits_left', 'credits_gifted', 'username', 'liked', 'disliked')
Bill Noble
  • 6,466
  • 19
  • 74
  • 133

4 Answers4

4

Your serializer doesn't have an owner field and your view doesn't provide one. As it's non null in your model the DB will complain about this.

You should override the view's perform_update and add the owner as extra argument to the serializer

Linovia
  • 19,812
  • 4
  • 47
  • 48
  • I am out of my depth with this. What would the perform_update need to look like to add the owner as an argument to the serializer? – Bill Noble Apr 04 '16 at 18:45
  • It's explained in the documentation linked - you need to scroll a bit and you'll have an example of it. – Linovia Apr 05 '16 at 10:38
1

You need to pass the Resource URI for the field which is foreign key, here owner is the FK, so the

ownerIns = User.objects.get(id=2)
let body = JSON.stringify({ url: 'fred', item_type: 'P', owner: ownerIns, owner_id: ownerIns.id });
Surajano
  • 2,688
  • 13
  • 25
0

I ran into a similar issue using Angular and Flask. This may because your CORS headers are not set correctly for your Django app which makes your Angular app not allowed to post to the backend. In Flask, I fixed it using this code:

@app.after_request
def after_request(response):
    response.headers.add('Access-Control-Allow-Origin', '*')
    response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
    response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
    return response

I'm not sure how to do this in Django, but this may be a great first stop for you since this is likely your issue.

Jaron Thatcher
  • 814
  • 2
  • 9
  • 24
0

Is your ItemSerializer code correct? The rest looks fine to me.

You should be having 'owner_id' in your serializer fields i think.

Take a look at this answer, add the related fields in this manner.

https://stackoverflow.com/a/20636415/5762482

Community
  • 1
  • 1
utkarshmail2052
  • 100
  • 1
  • 5