我有一个活动模型,每个活动都有不同的节目。
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模型。每张票都应该是唯一的。这意味着每张票都是唯一的,并且与Show和Seat相关。
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 frameowork。如何根据event_id
,start_date
,end_date
和time_list
创建显示的事件和date_time?
答案 0 :(得分:6)
我的方法有点不同。你在问号标签中说你正在使用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
变量。
此解决方案只是查看问题的另一种方式。
好运,祝你好运!答案 1 :(得分:4)
然后您的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
)
因此,您的模型Show
具有指定的DateFields
和数组TimeFields
。
django docs:Array Field,Time Field,Date Field
答案 2 :(得分:4)
日期时间库中有一些工具可以为您提供更简化的生成时间的方法。您可以使用toordinal
将日期转换为整数,并使用fromordinal
将整数转换回日期;这是创建一系列日期的好方法。您可以使用combine
将date
对象和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.
答案 3 :(得分:3)
我将离开验证部分,只专注于从给定数据生成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)
对现有代码进行了改进: