You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Django CharField添加自定义选项,支持新增用户输入选项

实现自定义选项的动态添加与接受

根据你的需求,下面提供两种可行方案,分别适用于临时运行时场景和持久化存储场景:

方案一:运行时动态添加(非持久化,重启后重置)

适合不需要长期保留自定义选项的场景,仅当前服务器运行期间有效。

1. 修改模型验证逻辑

在models.py中给JobPost添加自定义验证,跳过默认的choices校验,同时将新输入的选项追加到DAYS_CHOICE:

from django.core.exceptions import ValidationError
from constants import DAYS_CHOICE

class JobPost(Timestampable, SoftDeletes):
    id = models.UUIDField(primary_key=True, unique=True, editable=False, default=uuid.uuid4)
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=255, validators=[CustomLengthValidator(min_length=2, max_length=255, field_name='title')]) 
    schedule_days = models.CharField(max_length=256, choices=DAYS_CHOICE)

    def clean(self):
        super().clean()
        input_val = self.schedule_days.strip()
        existing_values = [val for _, val in DAYS_CHOICE]
        
        if not input_val:
            raise ValidationError("Schedule days cannot be empty.")
        
        # 若输入不在现有选项中,添加到列表末尾
        if input_val not in existing_values:
            # 自定义label和value格式,可根据需求调整
            DAYS_CHOICE.append((input_val.title(), input_val.lower()))

2. 表单支持自由输入

如果使用ModelForm,需要将字段改为CharField以允许用户自由输入:

from django import forms
from .models import JobPost
from constants import DAYS_CHOICE

class JobPostForm(forms.ModelForm):
    schedule_days = forms.CharField(
        max_length=256,
        widget=forms.TextInput(attrs={'placeholder': 'Enter schedule or choose from options'}),
        required=True
    )

    class Meta:
        model = JobPost
        fields = ['title', 'schedule_days', ...]

    def clean_schedule_days(self):
        data = self.cleaned_data['schedule_days'].strip()
        existing_values = [val for _, val in DAYS_CHOICE]
        if data not in existing_values:
            DAYS_CHOICE.append((data.title(), data.lower()))
        return data

注意:该方案中自定义选项仅在当前服务器进程中有效,重启服务器后会恢复为constants.py的初始值,多进程部署时各worker的选项列表相互独立。

方案二:持久化自定义选项(数据库存储,永久保存)

适合需要长期保留自定义选项的场景,所有选项存储在数据库中,重启服务器不会丢失。

1. 创建ScheduleDay模型

用数据库表替代模块级常量,存储所有日程选项:

# models.py
from django.db import models

class ScheduleDay(models.Model):
    name = models.CharField(max_length=256, unique=True, verbose_name="Schedule Name")
    slug = models.SlugField(max_length=256, unique=True, editable=False)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = self.name.lower().replace(' ', '-')
        super().save(*args, **kwargs)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "Schedule Days"

2. 修改JobPost模型关联

将原CharField替换为ForeignKey关联新模型:

class JobPost(Timestampable, SoftDeletes):
    id = models.UUIDField(primary_key=True, unique=True, editable=False, default=uuid.uuid4)
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=255, validators=[CustomLengthValidator(min_length=2, max_length=255, field_name='title')]) 
    schedule_days = models.ForeignKey(ScheduleDay, on_delete=models.CASCADE)

3. 初始化默认选项

在应用启动时自动创建默认的日程选项,修改apps.py:

# apps.py
from django.apps import AppConfig
from .models import ScheduleDay

class YourAppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'your_app_name'

    def ready(self):
        default_schedules = [
            ("Monday - Friday", "monday-friday"),
            ("Monday - Saturday", "monday-saturday"),
            ("Flexible", "flexible"),
        ]
        for name, slug in default_schedules:
            ScheduleDay.objects.get_or_create(slug=slug, defaults={'name': name})

4. 表单处理自定义输入

创建支持自定义输入的ModelForm,自动创建新的ScheduleDay实例:

from django import forms
from .models import JobPost, ScheduleDay

class JobPostForm(forms.ModelForm):
    schedule_days = forms.CharField(
        max_length=256,
        widget=forms.TextInput(attrs={'placeholder': 'Enter schedule (e.g. "Sunday Only")'}),
        required=True
    )

    class Meta:
        model = JobPost
        fields = ['title', 'schedule_days', ...]

    def clean_schedule_days(self):
        schedule_name = self.cleaned_data['schedule_days'].strip()
        # 不区分大小写查找,避免重复创建
        schedule_day, created = ScheduleDay.objects.get_or_create(
            name__iexact=schedule_name,
            defaults={'name': schedule_name}
        )
        return schedule_day

    def save(self, commit=True):
        self.instance.schedule_days = self.cleaned_data['schedule_days']
        return super().save(commit=commit)

该方案中所有自定义选项都会永久保存到数据库,多进程部署时也能共享选项列表,是更稳定的长期解决方案。

内容的提问来源于stack exchange,提问作者Amith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 03:12:12