实现DjangoORMadminview中model字段choices取值⾃动更新
的⼀种⽅法
有两个表,⼀个是记录⽹站信息的site表,结构如下:
CREATE TABLE `site` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
`url` varchar(128) NOT NULL,
`mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `url` (`url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置⽹站表'
⼀个是记录⽤户信息的user表,结构如下:
CREATE TABLE `user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`site_id` bigint(20) unsigned NOT NULL COMMENT 'site.id',
`user_id` varchar(32) NOT NULL,
`name` varchar(128) NOT NULL,
`description` text NOT NULL,
`mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置⽤户表'
如上⾯的表结构所⽰,user表中的site_id字段(简写为user.site_id)的取值其实仅限于site表中的id字段(简写为site.id)的取值,⼀种实现⽅式是在user.site_id和user.id两个字段上定义外键约束,但是如果由于某些原因,不能定义外键约束的话,可以通过在Django后台的model代码中,通过user.site_id的choices字段限制user.site_id的可取值范围,model中的代码如下所⽰:
# coding=utf-8
from__future__import unicode_literals
from django.db import models
class Site(models.Model):
id = models.PositiveIntegerField(primary_key=True, blank=True)
name = models.CharField(max_length=32, verbose_name=u'⽹站名称')
url = models.CharField(max_length=255, verbose_name=u'⽹址')
mtime = models.DateTimeField(auto_now=True, verbose_name=u'修改时间')
ctime = models.DateTimeField(auto_now=True,  verbose_name=u'创建时间')
class Meta:
db_table = 'site'
def get_site_choices():
rcs = Site.objects.all()
choices = [(x.id, x.name) for x in rcs]
return choices
class User(models.Model):
id = models.PositiveIntegerField(primary_key=True, blank=True, verbose_name=u'⾃增id(留空⾃动⽣成)')
site_id = models.PositiveIntegerField(verbose_name=u'⽹站', choices=get_site_choices())
user_id = models.CharField(max_length=32, verbose_name=u'⽤户id')
name = models.CharField(max_length=128, verbose_name=u'⽤户名')
description = models.TextField(verbose_name=u'备注')
mtime = models.DateTimeField(auto_now=True, verbose_name=u'修改时间')
ctime = models.DateTimeField(auto_now=True,  verbose_name=u'创建时间')
class Meta:
db_table = 'user'
在Django后台中创建两个ORM的admin view,假设site表中已经插⼊了以下两条数据:
那么在user表中执⾏ADD USER操作时,site_id的可选值就是⾖瓣电影和⾖瓣读书两个了
增加⼀个属于⾖瓣读书旗下的⽤户帐号后user表如下:
⽬前看来⼀切正常,但是如果在site表⾥⾯增加⼀个⽹站,⽐如⾖瓣⾳乐后,site表⾥⾯会更新为⾖瓣读书、⾖瓣电影、⾖瓣⾳乐三条记录,然⽽这时要是想在user表中再添加⼀条记录,site_id的下拉列表中却依然只有⾖瓣读书、⾖瓣电影两个取值:
这是因为class User中的 site_id = models.PositiveIntegerField(verbose_name=u'⽹站', choices=get_site_choices()) 这条语句只会在服务启动时类初始化的时候执⾏⼀次,这个时候会执⾏get_site_choices函数,将当时site.id的取值都拿出来作为user.site_id的choices,⽽在site表中新增或者减少了记录后,由于User类中的初始化语句并不会重新执⾏,所以会存在两个取值不⼀致的的问题,这种情况下要想更新user.site_id的取值,只能重启服务了。
然⽽每次更新了site表后,都需要重启服务的话那就完全不可接受了,解决⽅案是在class的__init__⽅法中,每次重新查询site表中的有效取值,⽽后重新给user.site_id字段的choices字段赋值,获取user.site_id字段是通过_field函数实现的,代码如下:
class User(models.Model):
id = models.PositiveIntegerField(primary_key=True, blank=True, verbose_name=u'⾃增id(留空⾃动⽣成)')
site_id = models.PositiveIntegerField(verbose_name=u'⽹站', choices=get_site_choices())
user_id = models.CharField(max_length=32, verbose_name=u'⽤户id')
name = models.CharField(max_length=128, verbose_name=u'⽤户名')
description = models.TextField(verbose_name=u'备注')
mtime = models.DateTimeField(auto_now=True, verbose_name=u'修改时间')
ctime = models.DateTimeField(auto_now=True,  verbose_name=u'创建时间')
def__init__(self, *args, **kargs):
super(User, self).__init__(*args, **kargs)
self.__field('site_id').choices = get_site_choices()
class Meta:
db_table = 'user'
对于get_field函数的说明参考Django⽂档
(docs.djangoproject/en/2.0/ref/models/meta/#dels._field):
<_field(field_name)
Returns the field instance given a name of a field.
field_name can be the name of a field on the model, a field on an abstract or inherited model, or a field defined on another model that points to the model. In the latter case, the field_name will be the related_name defined by the user or the name automatically generated by Django itself.
django admin 自定义页面cannot be retrieved by name.
If a field with the given name is not found a  exception will be raised.
如此在每⼀个user实例初始化时都会重新获取最新的site.id字段的取值,赋给user.site_id的choices属性,这样的缺点是每个实例初始化都会调⽤get_site_choices并对user.site_id.choices重新赋值,当⼀个页⾯加载的实例很多或者site表记录很多的时候,会存在性能问题,因⽽仅适合后台数据量较少的情况。
如下为在site表中新插⼊⾖瓣阅读⽹站后,在不重启服务的情况下,ADD USER页⾯中⾃动更新为最新choices的效果:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。