【问题标题】:Modelserializer using kwargs to get FK objectModelserializer 使用 kwargs 获取 FK 对象
【发布时间】:2016-09-14 14:04:50
【问题描述】:

我正在创建一个 Django (1.8) 网络应用程序,用于保存赛车圈速和记分牌。该数据库是使用使用 Django Rest Framework 构建的 API 填充的。这是我第一次尝试使用 rest 框架构建适当的 api。

模型概览:

  • 活动,赛车活动/周末
  • 会议,单场比赛/练习/资格赛 - FK 赛事
  • 汽车,参加会话的汽车 - FK 会话
  • 圈数,特定汽车的圈数 - FK 车

事件是手动创建的,但其余的应该是“动态的”(获取或创建)

现在我正在尝试使用我的 API 创建一辆新车,但我被卡住了。为了获得汽车事件和会话,我正在尝试使用 url; /api/results/skrotbilsracet-29042016/r1/cars/

这个想法是将数据发布到这个 url 并“获取或创建”一个新的汽车对象。 要为新车会话 FK 获取正确的会话对象,我需要使用一个自定义函数来获取 kwargs 并尝试查找会话。

我越了解如何解决这个问题,我就越困惑。

有人能把我推向正确的方向吗?

这是我解决这个问题的最新尝试,它只给了我“{“session”:[“This field is required.”]}”

models.py

class Session(models.Model):
    session_types = (
        ('p', 'Practice'),
        ('q', 'Qualification'),
        ('r', 'Race')
    )
    event_id = models.ForeignKey(Event, related_name='sessions')
    name = models.CharField(max_length=2, blank=True)
    current_session = models.BooleanField(default=True)
    session_type = models.CharField(max_length=2,
                                    choices=session_types)
    started = models.DateTimeField(auto_now_add=True)
    ended = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['started']

    def save(self):
        if not self.name:
            # Get number of sessions
            session_count = Session.objects.filter(event_id=self.event_id)\
                            .filter(session_type=self.session_type)\
                            .count()
            session_count += 1
            self.name = self.session_type + str(session_count)

        super(Session, self).save()

    def __unicode__(self):
        string = self.started.strftime("%d-%m-%Y %H:%M") + ' - '
        string += self.name.upper()
        return(string)


class Car(models.Model):
    session = models.ForeignKey(Session, related_name='cars')
    number = models.IntegerField()
    full_name = models.CharField(max_length=256, blank=True)
    short_name = models.CharField(max_length=256, blank=True)
    race_class = models.CharField(max_length=50, blank=True)
    best_lap = models.IntegerField(blank=True, null=True)
    best_lap_time = models.CharField(max_length=20, blank=True)
    best_sector1 = models.CharField(max_length=20, blank=True)
    best_sector2 = models.CharField(max_length=20, blank=True)
    best_sector3 = models.CharField(max_length=20, blank=True)
    best_speed = models.IntegerField(blank=True, null=True)
    pitstops = models.IntegerField(blank=True, null=True)
    total_time = models.CharField(max_length=20, blank=True)
    transponder = models.CharField(max_length=50, blank=True)

apiUrls.py

urlpatterns = [
  url(r'^raceslug/$', raceSlugView.as_view(), name='race-slug'),
  url(r'^events/$', eventsView.as_view(), name='event-list'),
  url(r'^session/$', getSessionView.as_view(), name='session-pk'),
  url(r'^(?P<event_id>[a-z0-9\-]+)/$', eventView.as_view(), name='event-detail'),
  url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/$', sessionView.as_view(), name='session-detail'),
  url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/cars/$', carsView.as_view(), name='car-list'),
  url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/(?P<number>[0-9]+)/$', carView.as_view(), name='car-detail'),
]

urlpatterns = format_suffix_patterns(urlpatterns)

api.py

class carsView(generics.ListCreateAPIView):
    serializer_class = carSerializer

    def get_session(self, event_id, name):
        print('Getting session')
        # Get event object
        try:
            event = Event.objects.get(event_id=event_id)
            print('Found event')
        except ObjectDoesNotExist:
            print('Did not find event')
            return

        # Get session object
        try:
            session = event.sessions.get(name=name)
            print('Found session: ', session)
            return session
        except ObjectDoesNotExist:
            print('Did not find session')
            return


    def get_queryset(self):
        print('Getting queryset')
        print('event_id: ' + self.kwargs['event_id'])
        print('name: ' + self.kwargs['name'])
        session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
        return(Car.objects.filter(session=session.pk))


    def perform_create(self, serializer):
        print('Creating new car')
        session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
        serializer.save(session=session)

serializers.py

class carSerializer(serializers.ModelSerializer):

  laps = lapSerializer(many=True, read_only=True)

  class Meta:
    model = Car
    fields = (
        'session',
        'number',
        'full_name',
        'short_name',
        'race_class',
        'best_lap',
        'best_lap_time',
        'best_sector1',
        'best_sector2',
        'best_sector3',
        'best_speed',
        'pitstops',
        'total_time',
        'transponder',
        'laps')

解决方案:
这是我实际更改以使其正常工作的内容。

api.py

from rest_framework.serializers import ValidationError

class carsView(generics.ListCreateAPIView):
...
    def perform_create(self, serializer):
        print('Creating new car')
        session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
        number = self.request.POST.get('number')
        car = session.cars.filter(number=number)
        if car.exists():
            raise ValidationError('Car already exists')
        serializer.save(session=session)

serializers.py

class carSerializer(serializers.ModelSerializer):

    laps = lapSerializer(many=True, read_only=True)
    session = serializers.StringRelatedField(required=False)
    ...

【问题讨论】:

  • 在您的汽车模型中添加空白=True,或在发布汽车时提供会话 ID(或超链接,如果它是 HyperlinedModelSerializer)。
  • 这是我的实际问题,提供从 url 中提取的会话 id 的工作方式是什么(可能使用我的 get_session 函数)?该会话是必需的,否则我只会让汽车物体漂浮在周围,而不会与任何实际比赛相关。
  • 您使用什么来访问您的 API?任何API客户端或smth?还是 DRF 的 API 浏览器?如果是 DRF 的浏览器,那么应​​该有一个张贴表格,您应该可以在那里看到它。如果某些客户端,例如 Pycharm 的客户端或 Postman,那么您必须将其添加到 POST 参数中(在 Postman 中,当您选择 POST 方法时,它位于 Body 中
  • 我更新了我的答案。我将其添加到答案中是因为我想在那里粘贴代码

标签: django django-rest-framework


【解决方案1】:

我看到您正在那里创建会话 ID:

def get_queryset(self):
       ...
        session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
        return(Car.objects.filter(session=session.pk))

那么您不需要在序列化程序中使用它,只需要在模型中使用它。因此,您可以将其设置为序列化程序中所需的鼻涕,但在模型中仍然需要它。

我想这个答案可以帮助你:Django REST Framework serializer field required=false

【讨论】:

  • 我没有创建我的会话 ID,我通过向我的“get_session”函数提供事件名称和会话名称来获取现有会话对象。这些展位是来自 url 的 kwargs,在这个例子中:r'^(?P&lt;event_id&gt;[a-z0-9\-]+)/(?P&lt;name&gt;[a-z0-9\-]+)/cars/$' event = 'skrotbilsracet-29042016' session = 'r1' get_session 函数返回一个会话对象。通过 API 创建新汽车对象时,如何将此会话对象用作会话 FK?它适用于您在上面粘贴的 get_queryset 方法(GET),但我无法让它用于创建新车(POST)。
  • 是的,但是您不是从浏览器的 HTTP 的 POST 方法中获取它。所以你在你的应用程序中创建它。当您没有从客户端完成的 POST 中获得会话时,会引发此错误。发布新车时,您应该参考我提供的链接。您也可以尝试在序列化程序中将会话变量标记为只读,但不确定它是否有帮助,它仍然需要它。此外,如果您正在创建会话并将其提供给模型,则可以将其标记为空白=True(但不是 null=True),以便表单不需要它。
  • 换句话说,如果它是按要求设置的,那么表单在来自 POST 的原始数据中需要它(而不是作为计算字段)。您的会话字段不是来自 HTTP POST,而是您计算出来的。所以它在 POST 中错过了它并抱怨。
  • 现在我终于明白了 :-) 谢谢你的帮助!将用细节更新我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-31
  • 2016-02-13
  • 1970-01-01
  • 2023-02-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多