【问题标题】:Create objects based on date and time根据日期和时间创建对象
【发布时间】:2018-07-25 20:48:00
【问题描述】:

我有一个 Event 模型,每个事件会有不同的节目。

class Event(models.Model):
    title = models.CharField(max_length=200)

class Show(models.Model):
    event = models.ForeignKey(Event, on_delete=models.CASCADE)
    date_time = models.DateTimeField(unique=True)

我有另一个 Ticket 模型。每张票都应该是独一无二的。这意味着每张门票都是独一无二的,并且与表演和座位相关。

class Ticket(models.Model):
    show = models.ForeignKey(Show)
    seat = models.ForeignKey(Seat)

    class Meta:
        unique_together = ('show', 'seat')

我需要根据用户提供的开始日期和结束日期创建节目。假设这是一个 JSON 帖子:

{
    "event_id": 1,
    "start_date": "2018-02-16",
    "end_date": "2018-02-20",
    "time_list": ["11:00 AM", "8:00 PM"]
}

从上面的 JSON 示例中,我需要像这样开始创建 Show:

# Start with the start_date as the date, and for each time from the time_list
Show.objects.create(
    event = 1,
    date_time = datetime.strptime('2018-02-16 11:00 AM', "%Y-%m-%d %I:%M %p")
)
Show.objects.create(
    event = 1,
    date_time = datetime.strptime('2018-02-16 8:00 PM', "%Y-%m-%d %I:%M %p")
)
# Next date after the start_date, i.e., 16+1 = 17
Show.objects.create(
    event = 1,
    date_time = datetime.strptime('2018-02-17 8:00 PM', "%Y-%m-%d %I:%M %p")
)
.
.
.
# Create Show objects till the end_date and for each time from the time_list
Show.objects.create(
    event = 1,
    date_time = datetime.strptime('2018-02-20 8:00 PM', "%Y-%m-%d %I:%M %p")
)

现在这就是我创建 Show 对象的方式:

def create_show_by_datetime(self, request):
    event_id = request.data['event_id']
    try:
        event = Event.objects.get(id=event_id)
    except Event.DoesNotExist:
        return Response(
            {'error': 'event with id: %s does not exist.' % event_id},
            status=status.HTTP_400_BAD_REQUEST
        )

    start_date = request.data['start_date']
    end_date = request.data['end_date']
    time_list = request.data['time_list']
    date_format = '%Y-%m-%d'
    time_format = "%I:%M %p"
    try:
        datetime.strptime(start_date, date_format)
        datetime.strptime(end_date, date_format)
        for i in range(len(time_list)):
            time = datetime.strptime(time_list[i], time_format)
    except ValueError as e:
        return Response(
            {'error': 'Time was not in a supported format. %s' % e},
            status=status.HTTP_400_BAD_REQUEST
        )

    delta_days = datetime.strptime(end_date, date_format).date() - datetime.strptime(start_date, date_format).date()
    delta_days = delta_days.days + 1
    dt = None
    try:
        with transaction.atomic():
            for i in range(delta_days):
                day = datetime.strptime(start_date, date_format) + timedelta(days=i)
                for i in range(len(time_list)):
                    hrs = datetime.strptime(time_list[i], time_format).hour
                    mins = datetime.strptime(time_list[i], time_format).minute
                    dt = day + timedelta(hours=hrs, minutes=mins)
                    show = Show.objects.create(
                        event=event,
                        date_time=dt
                    )
            return Response({"data": 'Post succesfull'}, status=status.HTTP_201_CREATED)
    except IntegrityError as e:
        return Response(
            {
                'error': "event with date and time already exsits. %s-%s-%s at %s:%s" % (
                    dt.day, dt.month, dt.year, dt.hour, dt.minute),
                'detail': str(e)
            }, status=status.HTTP_400_BAD_REQUEST

但我希望有比我现在做的更优雅的方式。我正在使用 python 3、django 2 和 django rest 框架。如何根据event_idstart_dateend_datetime_list 创建带有事件的Show和日期时间?

【问题讨论】:

  • 如何保存发帖数据???没有足够的字段将数据映射到数据库
  • @Ananthu 也许使用 for 循环?
  • 在您的有效载荷中,您传递了start_dateend_datetime_list。你能解释一下为什么会这样吗?
  • @JerinPeterGeorge 我已经更新了这个问题。请看一看。

标签: python django python-3.x django-rest-framework


【解决方案1】:

我的方法有点不同。您在问题标签中说您正在使用django-rest-framework.. 那么序列化程序在哪里? :)

让我们创建两个序列化器,一个用于用户数据验证(因为我们不信任 USER!),另一个用于多数据插入。

我还没有检查代码!但是你可以用它作为一个例子......

class ShowEventSerializer(serializers.Serializer):
    event_id = serializers.IntegerField()
    start_date = serializers.DateField(required=True)
    end_date = serializers.DateField(required=True)
    time_list = serializers.ListField(
        child=serializers.TimeField()
    )

    class Meta:
        fields = ('event_id', 'start_date', 'end_date', 'time_list')

class ShowSerializer(serializers.Serializer):
    date_time = serializers.DateTimeField()

    class Meta:
        model = Show
        fields = ('event', 'date_time')

现在,使用序列化器,我们将验证用户数据,然后创建一个 json 数据对象:

def create_show_by_datetime(self, request):

    show_event_serializer = ShowEventSerializer(data=request.data)
    if not show_event_serializer.is_valid():
        return Response({'error': show_event_serializer.errors},status=status.HTTP_400_BAD_REQUEST)

    event_id = show_event_serializer.data['event_id']
    try:
        event = Event.objects.get(id=event_id)
    except Event.DoesNotExist:
        return Response({'error': 'event with id: %s does not exist.' % event_id},status=status.HTTP_400_BAD_REQUEST)

    start_date = show_event_serializer.data['start_date']
    end_date = show_event_serializer.data['end_date']
    time_list = show_event_serializer.data['time_list']
    date_format = '%Y-%m-%d'
    time_format = "%I:%M %p"

    try:
        datetime.strptime(start_date, date_format)
        datetime.strptime(end_date, date_format)
        for i in range(len(time_list)):
            time = datetime.strptime(time_list[i], time_format)
    except ValueError as e:
        return Response(
            {'error': 'Time was not in a supported format. %s' % e},
            status=status.HTTP_400_BAD_REQUEST
        )

    delta_days = datetime.strptime(end_date, date_format).date() - datetime.strptime(start_date, date_format).date()
    delta_days = delta_days.days + 1
    dt = None
    show_data = []
    for i in range(delta_days):
        day = datetime.strptime(start_date, date_format) + timedelta(days=i)
        for i in range(len(time_list)):
            hrs = datetime.strptime(time_list[i], time_format).hour
            mins = datetime.strptime(time_list[i], time_format).minute
            dt = day + timedelta(hours=hrs, minutes=mins)
            show_data.append({
                "event": event,
                "date_time": dt
            })

    try:
        with transaction.atomic():
            show_serializer = ShowSerializer(data=show_data, many=True)
            if show_serializer.is_valid():
                show_serializer.save()

            return Response({"data": 'Post succesfull'}, status=status.HTTP_201_CREATED)
    except IntegrityError as e:
        return Response(
            {
                'error': "event with date and time already exsits. %s-%s-%s at %s:%s" % (
                    dt.day, dt.month, dt.year, dt.hour, dt.minute),
                'detail': str(e)
            }, status=status.HTTP_400_BAD_REQUEST

所以这段代码和你的基本相同,只是使用 DRF 保存对象的方式不同。查看show_data 变量。

这个解决方案只是看待问题的另一种方式。

祝你好运!

http://www.django-rest-framework.org/api-guide/serializers/

http://www.django-rest-framework.org/api-guide/fields/

【讨论】:

    【解决方案2】:

    那么你的Show 模型应该是这样的

    from django.contrib.postgres.fields import ArrayField
    
    class Show(models.Model):
        event = models.ForeignKey(Event, on_delete=models.CASCADE)
        start_date = models.DateField(blank=True, null=True)
        end_date = models.DateField(blank=True, null=True)
        board = ArrayField(
            models.TimeField(blank=True, null=True),
            size=10, # specify max array size
        )
    

    因此您将拥有具有指定 DateFieldsTimeFields 数组的模型 Show

    django 文档:Array FieldTime FieldDate Field

    【讨论】:

    • 实际上,我还有另一个 Ticket 模型。每张票都应该是独一无二的。这意味着每张门票都与一个节目和一个座位有关。因此我认为,最好有一个单独的节目来代替。我已经更新了问题,请看一下。
    【解决方案3】:

    日期时间库中有一些工具可以为您提供更简化的方法来生成时间。您可以使用toordinal 将日期转换为整数,使用fromordinal 将整数转换回日期;这是创建一系列日期的好方法。您可以使用combinedate 对象和time 对象合并为datetime。我会创建以下函数:

    from datetime import datetime, date
    
    def get_showtimes(post):
        start = datetime.strptime(post['start_date'], '%Y-%m-%d')
        end = datetime.strptime(post['end_date'], '%Y-%m-%d')
        times = [datetime.strptime(t, '%I:%M %p').time() for t in post['time_list']]
        for ordinal in range(start.toordinal(), end.toordinal() + 1):
            date = date.fromordinal(date)
            for time in times:
                yield datetime.combine(date, time)
    

    然后,在您的代码中,替换第二个 try: except: 块及其后面的内容:

    try:
        showtimes = list(get_showtimes(post))
    except ValueError as e:
        return Response(
            {'error': 'Time was not in a supported format. %s' % e},
            status=status.HTTP_400_BAD_REQUEST
        )
    
    try:
        with transaction.atomic():
            for showtime in showtimes:
                show = Show.objects.create(event=event, date_time=showtime)
    except IntegrityError as e:
        # etc.
    

    【讨论】:

      【解决方案4】:

      我将离开验证部分,只专注于从给定数据生成 Show 对象:

      data = request.data
      
      date_format = '%Y-%m-%d'
      time_format = "%I:%M %p"
      show_time_format = f"{date_format} {time_format}"
      
      # get the total number of days by parsing start and end dates
      start_date = datetime.strptime(data['start_date'], date_format)
      end_date = datetime.strptime(data['end_date'], date_format)
      total_days = (end_date - start_date).days + 1
      
      # Get the timings for the first day.
      # We will use this to generate the timings for the rest of the days.
      first_day_timings = [
          datetime.strptime(f"{data['start_date']} {show_time}", show_time_format)
          for show_time in data['time_list']
      ]
      
      # generate all show objects using list comprehension and bulk create later
      show_objects = [
          Show(event=event, date_time=first_day_timing + timedelta(days=day_cnt))
          for day_cnt in range(total_days)
          for first_day_timing in first_day_timings
      ]
      
      Show.objects.bulk_create(show_objects)
      

      对现有代码的改进:

      1. 减少了解析日期/时间和计算小时/分钟的实例数。
      2. 使用列表推导生成显示对象和批量创建,而不是在事务中一次创建一个对象。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-04
        • 2018-05-04
        • 2021-08-06
        • 2020-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多