Укажите точные часы работы предприятия

Я пытаюсь создать пользовательское поле для модели django. Мне нужно хранить часы работы для каждого дня недели.

class Restaurant(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
city = models.CharField(max_length=15)
street = models.CharField(max_length=15)
house_number = models.CharField(max_length=3)
flat_number = models.CharField(max_length=3, default='0')
phone_number = models.CharField(max_length=12)
image = models.ImageField()
rating = models.FloatField(validators=[validate_rating_number])

def __str__(self):
    return f'{self.name}, {self.city}'

Мне нужно хранить часы работы в поле типа

opening_hours = models.CustomField()

Если вам не нужна отдельная таблица и вы используете базу данных PostgreSQL, вы можете объединить TimeField с ArrayField. Это выглядело бы примерно так.

from django.contrib.postgres.fields import ArrayField
from django.db import models

class Restaurant(models.Model):
    opening_hours = ArrayField(models.TimeField(), size=7)

Затем можно получить доступ к часам работы для любого дня недели, используя индекс. Приняв воскресенье за первый день недели, вы можете получить доступ к часам работы во вторник с помощью

restaurant.opening_hours[2]  # Sunday = 0, Monday = 1

Чтобы расширить эту идею, можно добавить индексы с именами в модель Restaurant, чтобы в коде было еще более очевидно, что именно вы получаете.

from django.contrib.postgres.fields import ArrayField
from django.db import models

class Restaurant(models.Model):
    SUN, MON, TUE, WED, THU, FRI, SAT = 0, 1, 2, 3, 4, 5, 6
    
    opening_hours = ArrayField(models.TimeField(), size=7)


r = Restaurant.objects.first()
r.opening_hours[r.TUE]

Вы можете определить собственное пользовательское поле, которое будет содержать все часы работы в день. Здесь мы будем использовать класс OpeningHours, который будет хранить время работы в день типа datetime.time, что означает, что ввод недопустимого времени, например 12:61, не пройдет проверку. Затем мы сохраним его как строку, разделенную пробелами, в поле базы данных opening_hours, например

12:00 15:00 18:00 18:30 09:00 12:00 08:45

Все, что нам нужно сделать, это сконфигурировать:

  • Сохранение пользовательского поля в базу данных из экземпляра типа OpeningHours в строку с разделителем пространства раз в день через get_prep_value()
  • Извлечение пользовательского поля из базы данных из строки, разделенной пробелами, в экземпляр типа OpeningHours через from_db_value()
  • .

models.py

from datetime import datetime

from django.core.exceptions import ValidationError


class OpeningHours:
    FORMAT = '%H:%M'

    def __init__(self, mon, tue, wed, thu, fri, sat, sun):
        self.mon = datetime.strptime(mon, self.FORMAT).time()
        self.tue = datetime.strptime(tue, self.FORMAT).time()
        self.wed = datetime.strptime(wed, self.FORMAT).time()
        self.thu = datetime.strptime(thu, self.FORMAT).time()
        self.fri = datetime.strptime(fri, self.FORMAT).time()
        self.sat = datetime.strptime(sat, self.FORMAT).time()
        self.sun = datetime.strptime(sun, self.FORMAT).time()

    def __str__(self):
        return ' '.join(
            dt_time.strftime(self.FORMAT)
            for dt_time in (
                self.mon,
                self.tue,
                self.wed,
                self.thu,
                self.fri,
                self.sat,
                self.sun,
            )
        )


class OpeningHoursField(models.CharField):
    description = "Opening time per day"

    def from_db_value(self, value, expression, connection):
        return self.to_python(value)

    def to_python(self, value):
        if value is None or isinstance(value, OpeningHours):
            return value
        try:
            obj = OpeningHours(*value.split())
        except Exception:
            raise ValidationError("Invalid opening time")
        else:
            return obj

    def get_prep_value(self, value):
        return str(value)


class Restaurant(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=30)
    city = models.CharField(max_length=15)
    opening_hours = OpeningHoursField(max_length=100)

    def __str__(self):
        return f'{self.name}, {self.city}'

Создание нового Restaurant потребует установки объекта OpeningHours в пользовательское поле opening_hours:

>>> from my_app.models import *
>>> opening_hours = OpeningHours(mon="12:00", tue="15:00", wed="18:00", thu="18:30", fri="09:00", sat="12:00", sun="08:45")
>>> obj = Restaurant.objects.create(name="Burger King", city="London", opening_hours=opening_hours)
>>> obj.save()
>>> obj.__dict__
{'_state': <django.db.models.base.ModelState object at 0x7fe3c5b4cb50>, 'id': 1, 'name': 'Burger King', 'city': 'London', 'opening_hours': <my_app.models.OpeningHours object at 0x7fe3c5b4c1f0>}

Чтение поля opening_hours происходит так же, как и чтение обычного объекта OpeningHours:

>>> print(obj.opening_hours)
12:00 15:00 18:00 18:30 09:00 12:00 08:45
>>> obj.opening_hours.mon
datetime.time(12, 0)
>>> obj.opening_hours.tue
datetime.time(15, 0)
>>> obj.opening_hours.wed
datetime.time(18, 0)
>>> obj.opening_hours.thu
datetime.time(18, 30)
>>> obj.opening_hours.fri
datetime.time(9, 0)
>>> obj.opening_hours.sat
datetime.time(12, 0)
>>> obj.opening_hours.sun
datetime.time(8, 45)

Чтобы обновить время для определенного дня, просто обновите соответствующее поле и сохраните.

>>> # Display old value
>>> print(Restaurant.objects.first().opening_hours)
12:00 15:00 18:00 18:30 09:00 12:00 08:45
>>> print(Restaurant.objects.first().opening_hours.thu)
18:30:00
>>>
>>> # Update new value for Thursday and save
>>> obj.opening_hours.thu = datetime.time(hour=1, minute=23)
>>> obj.save()
>>>
>>> # Display updated value
>>> print(Restaurant.objects.first().opening_hours)
12:00 15:00 18:00 01:23 09:00 12:00 08:45
>>> print(Restaurant.objects.first().opening_hours.thu)
01:23:00
Вернуться на верх