im trying to create custom field for django model. I need to store opening hours for every day of the week.
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}'
I have to store opening hours in a field like
opening_hours = models.CustomField()
CodePudding user response:
If you don't want a separate table and you are using a postgreSQL database, you could combine the TimeField with an ArrayField. It would look something like this.
from django.contrib.postgres.fields import ArrayField
from django.db import models
class Restaurant(models.Model):
opening_hours = ArrayField(models.TimeField(), size=7)
You would then access the hours for any day of the week using an index. Assuming Sunday as the first day of the week you could access Tuesday's opening hours with
restaurant.opening_hours[2] # Sunday = 0, Monday = 1
To expand on this idea, you could add the indexes with names to the Restaurant model so that it would be even more obvious in your code exactly what you're getting.
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]
CodePudding user response:
You can define your own custom field that will hold all the opening hours per day. Here, we will use an OpeningHours
class that will hold the opening time per day of type datetime.time which means entering an invalid time e.g. 12:61
would fail validation. Then, we will store it as a space-separated string to the database field opening_hours
e.g.
12:00 15:00 18:00 18:30 09:00 12:00 08:45
All we need to do is to configure:
- Saving of the custom field to the database from an instance of type
OpeningHours
to that space-separated string of times per day viaget_prep_value()
- Retrieving the custom field from the database from that space-separated string to an instance of
OpeningHours
viafrom_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}'
Creating a new Restaurant
would require an OpeningHours
object to be set to the custom opening_hours
field:
>>> 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>}
Reading the opening_hours
field is just like reading an ordinary OpeningHours
object:
>>> 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)
To update the time for a particular day, just update the corresponding field and save.
>>> # 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