Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django-Rest-Framework updating a foreign key BY Id

I am using django-rest-framework to build out the back end. I have the list running fine, but (using the django-rest-framework admin screen) I cannot create an object by just using the Id fields of the foreign key objects. I hope I have this configured incorrectly, but I am open to writing some code if i have to :) I am learning django/python from a .NET and Java background and may have become a touch spoiled by this new stack.

Edit: I am trying not to use two different Model classes- I shouldn't have to right?

Thanks in advance.

From Chrome - the key bits of the request

Request URL:http://127.0.0.1:8000/rest/favorite_industries/ 
Request Method:POST 
_content_type:application/json
_content:{
    "user_id": 804    ,"industry_id": 20 }

The response

HTTP 400 BAD REQUEST
Vary: Accept
Content-Type: text/html; charset=utf-8
Allow: GET, POST, HEAD, OPTIONS

{
    "user": [
        "This field is required."
    ]
}

Ugh. Here are the key classes from django:

class FavoriteIndustry(models.Model):
    id = models.AutoField(primary_key=True)
    user = models.ForeignKey(User, related_name='favorite_industries')
    industry = models.ForeignKey(Industry)

    class Meta:
        db_table = 'favorites_mas_industry'

class FavoriteIndustrySerializer(WithPkMixin, serializers.HyperlinkedModelSerializer):
    class Meta:
        model = myModels.FavoriteIndustry
        fields = (
            'id'
            , 'user'
            , 'industry'
        )

Edit Adding the viewset:

class FavoriteIndustriesViewSet(viewsets.ModelViewSet):
    #mixins.CreateModelMixin, viewsets.GenericViewSet):
    paginate_by = 1
    queryset = myModels\
        .FavoriteIndustry\
        .objects\
        .select_related()
    print 'SQL::FavoriteIndustriesViewSet: ' + str(queryset.query)
    serializer_class = mySerializers.FavoriteIndustrySerializer

The get/list functionality generates decent JSON:

{"count": 2, "next": "http://blah.com/rest/favorite_industries/?page=2&format=json", "previous": null, "results": [{"id": 1, "user": "http://blah.com/rest/users/804/", "industry": {"industry_id": 2, "industry_name": "Consumer Discretionary", "parent_industry_name": "Consumer Discretionary", "category_name": "Industries"}}]}

like image 214
akaphenom Avatar asked Oct 16 '13 16:10

akaphenom


People also ask

How do you save a foreign key field in Django Rest Framework?

Your code is using serializer only for validation, but it is possible use it to insert or update new objects on database calling serializer. save() . To save foreign keys using django-rest-framework you must put a related field on serializer to deal with it. Use PrimaryKeyRelatedField .

What is the difference between ModelSerializer and HyperlinkedModelSerializer?

The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.

What is renderers in Django Rest Framework?

The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client. REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types.

Which authentication is best in Django Rest Framework?

Django-Knox is a framework that makes the authentication of the API endpoints built with the Django Rest Framework easier. However, Knox is also a token-based authentication like JSON Web Token (JWT) auth. Django-Knox comes with well-detailed documentation for easy implementation.


1 Answers

I have created a simplified mock up of your application.

models.py:

from django.db import models
from django.contrib.auth.models import User

class Industry(models.Model):
    name = models.CharField(max_length=128)

class FavoriteIndustry(models.Model):
    user = models.ForeignKey(User, related_name='favorite_industries')
    industry = models.ForeignKey(Industry)

views.py:

from rest_framework import viewsets
from models import FavoriteIndustry
from serializers import FavoriteIndustrySerializer

class FavoriteIndustriesViewSet(viewsets.ModelViewSet):
    queryset = FavoriteIndustry.objects.all()
    serializer_class = FavoriteIndustrySerializer

serializers.py:

from rest_framework import serializers
from models import FavoriteIndustry, Industry

class FavoriteIndustrySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = FavoriteIndustry
        fields = ('id', 'user', 'industry')

urls.py:

from django.conf.urls import patterns, include, url
from core.api import FavoriteIndustriesViewSet

favorite_industries_list = FavoriteIndustriesViewSet.as_view({
    'get': 'list',
    'post': 'create'
})

urlpatterns = patterns('',
    url(r'^favorite_industries/$', favorite_industries_list, name='favorite-industries-list'),
    url(r'^users/(?P<pk>[0-9]+)/$', favorite_industries_list, name='user-detail'),
    url(r'^industries/(?P<pk>[0-9]+)/$', favorite_industries_list, name='industry-detail'),
)

And here are a few tests:

>>> 
>>> import json
>>> from django.test import Client
>>> from core.models import Industry
>>> 
>>> industry = Industry(name='candy')
>>> industry.save()
>>> 
>>> c = Client()
>>> 
>>> response = c.get('http://localhost:8000/favorite_industries/')
>>> response.content
'[]'
>>> 
>>> data = {
...     'user': 'http://localhost:8000/users/1/',
...     'industry': 'http://localhost:8000/industries/1/'
... }
>>> 
>>> response = c.post('http://localhost:8000/favorite_industries/', json.dumps(data), 'application/json')
>>> response.content
'{"id": 1, "user": "http://testserver/users/1/", "industry": "http://testserver/industries/1/"}'
>>> 
>>> response = c.get('http://localhost:8000/favorite_industries/')
>>> response.content
'[{"id": 1, "user": "http://testserver/users/1/", "industry": "http://testserver/industries/1/"}]'
>>> 

Django REST Framework expects user and industry fields as URLs rather than ids since you are using HyperlinkedModelSerializer.


Using IDs

In case you need to use object ids instead of URLs, use ModelSerializer instead of HyperlinkedModelSerializer and pass ids to user and industry:

serializers.py:

from rest_framework import serializers
from models import FavoriteIndustry, Industry

class FavoriteIndustrySerializer(serializers.ModelSerializer):
    class Meta:
        model = FavoriteIndustry
        fields = ('id', 'user', 'industry')

And tests:

>>> 
>>> import json
>>> from django.test import Client
>>> from core.models import Industry
>>> 
>>> #industry = Industry(name='candy')
>>> #industry.save()
>>> 
>>> c = Client()
>>> 
>>> response = c.get('http://localhost:8000/favorite_industries/')
>>> response.content
'[{"id": 1, "user": 1, "industry": 1}, {"id": 2, "user": 1, "industry": 1}]'
>>> 
>>> data = {
...     'user': 1,
...     'industry': 1
... }
>>> 
>>> response = c.post('http://localhost:8000/favorite_industries/', json.dumps(data), 'application/json')
>>> response.content
'{"id": 3, "user": 1, "industry": 1}'
>>> 
>>> response = c.get('http://localhost:8000/favorite_industries/')
>>> response.content
'[{"id": 1, "user": 1, "industry": 1}, {"id": 2, "user": 1, "industry": 1}, {"id": 3, "user": 1, "industry": 1}]'
>>> 
like image 78
Aziz Alfoudari Avatar answered Sep 27 '22 16:09

Aziz Alfoudari