【问题标题】:Django DRF Foreign KeyDjango DRF 外键
【发布时间】:2019-03-16 05:46:03
【问题描述】:

这个问题或许多类似问题已被多次询问,但由于某种原因我无法找到答案。

我确实有这个工作方式,如果你继续访问 api 页面,它会毫无问题地呈现、创建和更新。问题是显示嵌套对象中的字段(标题),而不仅仅是前端的主键。

进入代码之前的一些背景知识:

Races 是一个有限列表(例如 Race1、Race2、Race3),前端没有能力添加更多。

卡片不是有限的,但每张卡片都必须链接到现有的种族(目前通过主键这样做)。

前端应该显示链接比赛的card_text和比赛标题。 它还可以添加新卡,但这很好用。

我已经使用单独的序列化程序进行读取和创建/更新,其中读取具有“深度 = 1”以拉动整个对象,但创建/更新没有,然后您解析对象并发送主键返回(我在序列化程序中找不到这样做的方法,有可能吗?)。

所以基本上我的问题是,你是要传递整个对象并在 POST 方法上解析它,还是传递主键并拉入链接的对象(种族)并使用主键作为索引(例如 Races[card_race])。另外,为什么 'linked_race' 没有进入前端?

我意识到我几乎已经回答了自己的问题,但由于我是 Django 新手,我正在寻找正确的约定,谁知道呢,它可能会在寻找相同答案时节省其他人的时间。

urls.py

from .api import CardViewSet, RaceViewSet
from rest_framework.routers import DefaultRouter
from django.conf.urls import url, include
from .views import landing

router = DefaultRouter()
router.register(r'cards', CardViewSet)
router.register(r'races', RaceViewSet)

urlpatterns = [
    url(r'^$', landing),
    url(r'^api/', include(router.urls)),
]

api.py

from rest_framework.viewsets import ModelViewSet
from .serializers import CardSerializer, RaceSerializer
from .models import Card, Race


class CardViewSet(ModelViewSet):
    queryset = Card.objects.filter(active=True)

    def get_serializer_class(self):
        return CardSerializer

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


class RaceViewSet(ModelViewSet):
    queryset = Race.objects.filter(active=True)
    serializer_class = RaceSerializer

models.py

from django.db import models
from django.conf import settings

User = settings.AUTH_USER_MODEL

class Race(models.Model):
    id = models.IntegerField(primary_key=True)
    title = models.CharField(max_length=30, blank=False)
    active = models.BooleanField(default=True)

    def __str__(self):
        return "{}".format(self.title)

    def __unicode__(self):
        return self.title


class Card(models.Model):
    card_text = models.CharField(max_length=100, blank=False)
    card_description = models.CharField(max_length=100, blank=True)
    card_race = models.ForeignKey(Race, related_name='linked_race', on_delete=models.CASCADE)
    creator = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    active = models.BooleanField(default=True)

    def __str__(self):
        return self.card_text

    class Meta:
        ordering = ('created',)

serializers.py

from rest_framework import serializers
from .models import Card, Race


class RaceSerializer(serializers.ModelSerializer):

    class Meta:
        model = Race
        fields = '__all__'


class CardSerializer(serializers.ModelSerializer):
    linked_race = RaceSerializer(read_only=True, many=True)

    class Meta:
        model = Card
        fields = 'id', 'card_text', 'card_description', 'card_race', 'linked_race',

Javascript 提取 (AngularJS)

$http.get('/api/races/').then(function (response) {
  $scope.races = response.data;
  $scope.selectedOption = $scope.races[0];
});
$scope.cards = [];
$http.get('/api/cards/').then(function (response) {
  $scope.cards = orderBy(response.data, 'created', true);
});

html 提取 (AngularJS)

<div class="races--row" ng-repeat="c in cards | filter : card_filter |
orderBy : sortVal : sortDir" ng-class-odd="'odd'" ng-click="openModal(c)"> 

<div class="races--cell race">{{ c.card_race.title }}</div> 
<div class="races--cell card-text">{{ c.card_text }}</div> 

</div>

【问题讨论】:

    标签: angularjs django django-rest-framework


    【解决方案1】:

    您的第一个“问题”与Card 模型有关(我说问题是因为我认为您不打算这样做)。您正在为 card_race 字段定义 related_name='linked_race'。这个related_name 是您用来指代卡片FROM 种族的名称。

    我建议你不要使用它并使用 Django 已经提供给我们的默认值(即在这种情况下为 my_race.card_set.all())。因此,将Card 模型中的该字段更改为:

    class Card(models.Model):
        ...
        card_race = models.ForeignKey(Race, on_delete=models.CASCADE)
        ...
    

    让我们将卡序列化器更改为:

    class CardSerializer(serializers.ModelSerializer):
        # no more linked_race
        class Meta:
            model = Card
            fields = ('id', 'card_text', 'card_description', 'card_race')
    

    好的,这是一个不同的基本模型序列化程序,您还不会看到比赛的详细信息。所以现在让我们来解决您想要解决的主要问题:

    • 查看卡片相关种族的详细信息
    • 使用相同的序列化程序执行创建/获取/更新/删除操作

    为此,让我们进一步更改CardSerializer 以包含另一个名为race_detail 的字段:

    class CardSerializer(serializers.ModelSerializer):
        race_detail = RaceSerializer(source='card_race', read_only=True)
    
        class Meta:
            model = Card
            fields = ('id', 'card_text', 'card_description', 'card_race', 'race_detail')
    

    我们为同一个模型字段定义了两个序列化器字段。注意sourceread_only 属性。这使得该字段在您获取卡片时可用(这是我们想要的),但在您执行 POST 或 PUT 时不可用(这避免了发送整个比赛对象和解析等问题)。您只需发送 card_race 字段的比赛 ID,它应该可以工作。

    【讨论】:

    • 非常感谢!我按照建议进行了更改,但出现“'Race' object is not iterable”错误。删除“many=True”(或将其更改为 False)解决了这个问题,一切正常!显然前端需要更改为 'c.race_detail.title' 以通过不同的对象。
    • @mwade 啊,是的,Card 只有一个Race,所以不需要many=True。好修复!我已经用这个变化更新了我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-13
    • 1970-01-01
    相关资源
    最近更新 更多