django admin为model字段添加动态选项

在工作中遇到一个需求,在admin后台创建某个数据库model的实例时某个字段需要实时地从别的接口获取选项,一开始直接用的model.Field里的choices参数,如下:

from django.db import models
from .utils import get_choices

class Example(model.Model):
    text = models.CharField(choices=get_choices(), max_length=100, default="")

后来发现这样在admin后台创建新的实例时并不能获取到实时的choices,需要重启服务器后,choices才刷新,翻阅文档发现:

Note that choices can be any iterable object – not necessarily a list or tuple. This lets you construct choices dynamically. But if you find yourself hacking choices to be dynamic, you’re probably better off using a proper database table with a ForeignKey. choices is meant for static data that doesn’t change much, if ever.

文档提到,choices用于静态的选项,不适用动态的选项。看来不能使用choices,要想其他办法了。通过google发现,可以用自定义form的方式来添加动态选项,参考https://stackoverflow.com/questions/12626171/django-admin-choice-field

于是修改admin.py:

from django.contrib import admin
from django import forms
from .model import Example
from .utils import get_choices

class ExampleForm(forms.ModelForm)
    # 定义要设置动态选项的字段
    text = forms.ChoiceField(choices=get_choices())
    
    # 必须重写__init__方法,这样才能每次实例化表单时获取实时的选项
    def __init__(self, *args, **kwargs):
        super(ExampleForm, self).__init__(*args, **kwargs)
        self.fields['text'].choices = get_choices()
        
class ExampleAdmin(admin.ModelAdmin):
    form = ExampleForm

admin.site.register(Example, ExampleAdmin)

同时将model.py里的choices去掉:

from django.db import models
from .utils import get_choices

class Example(model.Model):
    text = models.CharField(max_length=100, default="")

这里有个问题,去掉choices后,实例对象就没有get_text_display这个方法了,所有还要自己写一个:

from django.db import models
from .utils import get_choices

class Example(model.Model):
    text = models.CharField(max_length=100, default="")
    
    def get_text_dispaly(self):
        '''
        choices的格式为[(u_id, u_name), (u_id, u_name)...]
        '''
        choices = get_choices()
        for choice in choices:
            if choices[0] == self.text:
                return choices[1]

经过这一番鼓捣,总算是满意了。

Show Comments