Django-Rest-Framework updating a foreign key BY Id

2020-02-26 09:08发布

问题:

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"}}]}

回答1:

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}]'
>>> 


回答2:

This will do it. But I think the django-rest-framework should provide this plumbing for me, so please follow up with any better answers

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

    def create(self, request):
        print(request.DATA)
        user_id = request.DATA['user_id']
        industry_id = request.DATA['industry_id']
        favorite = myModels.FavoriteIndustry(user_id=user_id, industry_id=industry_id)
        favorite.save()
        responseData = {
            'user_id': user_id
            , 'industry_id': industry_id
            , 'message': 'FavoriteIndustry saved.'
        }
        return Response(responseData)