带你进入异步Django+Vue的世界 - Didi打车实战(5)

上一篇 带你进入异步Django+Vue的世界 - Didi打车实战(4)
Demo: https://didi-taxi.herokuapp.com/

上一篇,前、后端已经完整支持了Websockets。
接下来,我们来实现创建订单、群发群收、修改订单功能。

Refactoring: Trip返回信息

后台返回Trip信息里,driver/rider是一个primary key,指向User。我们希望能直接看到ForeignKey: driver/rider的详细信息。

[{created: "2019-05-20T10:08:59.950536Z"
driver: null
drop_off_address: "牛首山"
id: "4a25dde1-dd0d-422a-9e5e-706958b65046"
pick_up_address: "总统府"
rider: {id: 5, username: "rider3", first_name: "", last_name: "", group: "rider"}
status: "REQUESTED"
updated: "2019-05-20T10:08:59.950563Z"}, ...]

Serializer添加ReadOnlyTripSerializer,关联UserSerializer即可。

# /backend/api/serializers.py
class ReadOnlyTripSerializer(serializers.ModelSerializer):
    driver = UserSerializer(read_only=True)
    rider = UserSerializer(read_only=True)

    class Meta:
        model = Trip
        fields = '__all__'

然后修改DRF view, 用户HTTP访问/trip/时的TripView,

#
class TripView(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'id'
    lookup_url_kwarg = 'trip_id'
    permission_classes = (permissions.IsAuthenticated,)
    queryset = Trip.objects.all()
    serializer_class = ReadOnlyTripSerializer  # changed

Channels 创建订单

当用户创建一个订单时,我们用Consumer来创建订单:

  • 判断消息type是否为create.trip
  • 调用DRFtrip = serializer.create()创建
  • 注意Django的数据库操作,都是同步的,而Channels是异步的,所以需要加个装饰器:@database_sync_to_async
  • 创建Trip记录后,再添加用户信息,调用ReadOnlyTripSerializer()
  • 发送Websockets: self.send_json()
  • 新订单创建时,通知所有的司机:channel_layer.group_send( group='drivers', message={ 'type': 'echo.message', 'data': trip_data } )
    • 其中'type': 'echo.message',Channels会自动调用echo_message(event)函数,保证在drivers组里的司机们都能收到
# api/consumers.py

from channels.db import database_sync_to_async # new
from channels.generic.websocket import AsyncJsonWebsocketConsumer

from api.serializers import ReadOnlyTripSerializer, TripSerializer # new


class TaxiConsumer(AsyncJsonWebsocketConsumer):
    # modified
    async def connect(self):
        user = self.scope['user']
        if user.is_anonymous:
            await self.close()
        else:
            channel_groups = []
            # Add a driver to the 'drivers' group.
            user_group = await self._get_user_group(self.scope['user'])
            if user_group == 'driver':
                channel_groups.append(self.channel_layer.group_add(
                    group='drivers',
                    channel=self.channel_name
                ))
            # Get trips and add rider to each one's group.
            self.trips = set([
                str(trip_id) for trip_id in await self._get_trips(self.scope['user'])
            ])
            for trip in self.trips:
                channel_groups.append(self.channel_layer.group_add(trip, self.channel_name))
            await asyncio.gather(*channel_groups)

            await self.accept()

    # new
    async def receive_json(self, content, **kwargs):
        message_type = content.get('type')
        if message_type == 'create.trip':
            await self.create_trip(content)

    # new
    async def echo_message(self, event):
        await self.send_json(event)
    # new
    async def create_trip(self, event):
        trip = await self._create_trip(event.get('data'))
        trip_id = f'{trip.id}'
        trip_data = ReadOnlyTripSerializer(trip).data
        # Send rider requests to all drivers.
        await self.channel_layer.group_send(
            group='drivers', message={
                'type': 'echo.message',
                'data': trip_data
            }
        )
        # Add trip to set.
        if trip_id not in self.trips:
            self.trips.add(trip_id)
            # Add this channel to the new trip's group.
            await self.channel_layer.group_add(
                group=trip_id, channel=self.channel_name
            )

        await self.send_json({
            'type': 'create.trip',
            'data': trip_data
        })

    # new
    @database_sync_to_async
    def _create_trip(self, content):
        serializer = TripSerializer(data=content)
        serializer.is_valid(raise_exception=True)
        trip = serializer.create(serializer.validated_data)
        return trip

前端 - 创建订单

点击导航条上的叫车按钮,显示对话框:


带你进入异步Django+Vue的世界 - Didi打车实战(5)_第1张图片
image.png