如何在Django模型中自定义字段存储商家一周每日营业时间
Django自定义营业时间字段实现方案
方案1:基于JSONField封装轻量实现(推荐)
该方案复用Django内置JSONField,仅需补充营业时间格式校验逻辑即可满足需求,无需完全重写字段核心逻辑,开发和维护成本最低。
兼容所有支持JSON字段的数据库(MySQL 5.7+、PostgreSQL、SQLite 3.9+等)
首先定义营业时间格式验证器:
from django.core.exceptions import ValidationError import datetime def validate_opening_hours(value): # 约定格式:键为1-7对应周一到周日,值为时段列表,每个时段包含open/close两个HH:MM格式的时间 if not isinstance(value, dict): raise ValidationError("营业时间必须为对象格式") # 校验必须包含7天的配置 for day in map(str, range(1, 8)): if day not in value: raise ValidationError(f"缺少周{day}的营业时间配置") if not isinstance(value[day], list): raise ValidationError(f"周{day}的营业时间必须为时段列表") # 校验每个时段的时间格式 for period in value[day]: if "open" not in period or "close" not in period: raise ValidationError(f"周{day}时段缺少开门/关门时间") for time_key in ["open", "close"]: try: datetime.datetime.strptime(period[time_key], "%H:%M") except ValueError: raise ValidationError(f"时间格式错误,必须为HH:MM格式")
在Restaurant模型中添加字段即可:
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]) # 新增营业时间字段 opening_hours = models.JSONField( default=dict, validators=[validate_opening_hours], help_text="格式:键为1-7代表周一到周日,值为时段列表,每个时段包含open和close时间,格式为HH:MM" ) def __str__(self): return f'{self.name}, {self.city}'
方案2:完全自定义独立字段类(符合要求的models.CustomField调用形式)
如果需要封装成独立的字段类型复用,可以继承Django的Field基类实现完整的自定义字段:
from django.db import models from django.core.exceptions import ValidationError import json import datetime # 复用方案1的验证器 def validate_opening_hours(value): if not isinstance(value, dict): raise ValidationError("营业时间必须为对象格式") for day in map(str, range(1, 8)): if day not in value: raise ValidationError(f"缺少周{day}的营业时间配置") if not isinstance(value[day], list): raise ValidationError(f"周{day}的营业时间必须为时段列表") for period in value[day]: if "open" not in period or "close" not in period: raise ValidationError(f"周{day}时段缺少开门/关门时间") for time_key in ["open", "close"]: try: datetime.datetime.strptime(period[time_key], "%H:%M") except ValueError: raise ValidationError(f"时间格式错误,必须为HH:MM格式") class OpeningHoursField(models.Field): description = "存储一周7天的营业时间" def __init__(self, *args, **kwargs): kwargs['default'] = dict super().__init__(*args, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() return name, path, args, kwargs def from_db_value(self, value, expression, connection): # 从数据库读取时转为Python字典 if value is None: return {} return json.loads(value) def to_python(self, value): # 反序列化为Python对象 if isinstance(value, dict): return value try: return json.loads(value) except (TypeError, ValueError): raise ValidationError("无效的营业时间格式") def get_prep_value(self, value): # 存入数据库前转为JSON字符串 validate_opening_hours(value) return json.dumps(value) def db_type(self, connection): # 数据库层存储为JSON类型 return 'json'
在模型中直接调用即可:
opening_hours = OpeningHoursField()
用法示例
# 赋值 restaurant = Restaurant.objects.create( # 其他字段赋值省略 opening_hours = { "1": [{"open": "09:00", "close": "22:00"}], "2": [{"open": "09:00", "close": "22:00"}], "3": [{"open": "09:00", "close": "22:00"}], "4": [{"open": "09:00", "close": "22:00"}], "5": [{"open": "09:00", "close": "23:00"}], "6": [{"open": "10:00", "close": "23:00"}], "7": [{"open": "10:00", "close": "22:00"}], } ) # 访问周六营业时间 print(restaurant.opening_hours["6"])
内容的提问来源于stack exchange,提问作者anderex92
相关产品推荐
相关产品推荐

