实现Django ORM admin view中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表中已经插入了以下两条数据:

实现Django ORM admin view中model字段choices取值自动更新的一种方法

那么在user表中执行ADD USER操作时,site_id的可选值就是豆瓣电影和豆瓣读书两个了

实现Django ORM admin view中model字段choices取值自动更新的一种方法

增加一个属于豆瓣读书旗下的用户帐号后user表如下:

实现Django ORM admin view中model字段choices取值自动更新的一种方法

目前看来一切正常,但是如果在site表里面增加一个网站,比如豆瓣音乐后,site表里面会更新为豆瓣读书、豆瓣电影、豆瓣音乐三条记录,然而这时要是想在user表中再添加一条记录,site_id的下拉列表中却依然只有豆瓣读书、豆瓣电影两个取值:

实现Django ORM admin view中model字段choices取值自动更新的一种方法

实现Django ORM admin view中model字段choices取值自动更新的一种方法

这是因为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字段是通过self.get_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._meta.get_field('site_id').choices = get_site_choices()

    class Meta:
        db_table = 'user'

对于get_field函数的说明参考Django文档(https://docs.djangoproject.com/en/2.0/ref/models/meta/#django.db.models.options.Options.get_field):

Options. get_field(field_name) [source]

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.

<span class="pre">Hidden&#xA0;<span class="pre">fields</span></span> cannot be retrieved by name.

If a field with the given name is not found a <span class="pre">FieldDoesNotExist</span> exception will be raised.

如此在每一个user实例初始化时都会重新获取最新的site.id字段的取值,赋给user.site_id的choices属性,这样的缺点是每个实例初始化都会调用get_site_choices并对user.site_id.choices重新赋值,当一个页面加载的实例很多或者site表记录很多的时候,会存在性能问题,因而仅适合后台数据量较少的情况。

如下为在site表中新插入豆瓣阅读网站后,在不重启服务的情况下,ADD USER页面中自动更新为最新choices的效果:

实现Django ORM admin view中model字段choices取值自动更新的一种方法

实现Django ORM admin view中model字段choices取值自动更新的一种方法

Original: https://www.cnblogs.com/AcAc-t/p/django_admin_model_choices_update.html
Author: 及时
Title: 实现Django ORM admin view中model字段choices取值自动更新的一种方法

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/578251/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Docker-网络模式

    Docker-网络模式 1.Docker网络模式概述 Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Doc…

    Linux 2023年6月13日
    075
  • Linux命令1

    快捷键 1&#x3001;&#x6253;&#x5F00;&#x7EC8;&#x7AEF; ctrl+alt+t 2&#x3001;…

    Linux 2023年6月8日
    0109
  • Shell语法

    在 Shell 中引号分为 2 种:单引号、双引号。 ( 1 )双引号 由双引号括起来的字符,除 $ 、倒引号和反斜线( \ )仍保留其特殊功能外,其余字符通常作为普通字符对待。 …

    Linux 2023年5月28日
    086
  • ECMAScript 6.0

    本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。 博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。 博主…

    Linux 2023年6月13日
    085
  • 截止2021年底,我国18个税种中已有12个税种完成立法

    截止2021年底,我国18个税种中已有12个税种完成立法: 1.中华人民共和国个人所得税法 (自1980年9月10日起施行)2.中华人民共和国企业所得税法 (自2008年1月1日起…

    Linux 2023年6月14日
    0401
  • JuiceFS V1.0 RC1 发布,大幅优化 dump/load 命令性能, 深度用户不容错过

    各位社区的伙伴, JuiceFS v1.0 RC1 今天正式发布了! 这个版本中,最值得关注的是对元数据迁移备份工具 dump/load 的优化。 这个优化需求来自于某个社区重度用…

    Linux 2023年6月14日
    079
  • Error: Unable to access jarfile 运行jar包报错

    1、可能是执行路径有误 错误 修改后,需使用绝对路径 2、决解1后,还报以下错误,就是你的安装的JDK与jar包中的JDK不是同一版本。idea中有自带的JDK与我们安装的JDK版…

    Linux 2023年6月14日
    0921
  • windows下使用route添加路由

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    0102
  • Git工作流程

    学于2018年6月 总的流程: 一: 首先克隆整个项目到本地 二: 在本地创建一个属于自己的分支, 并push到远程(当时的工作情况是, 每实现一个功能, 或修改一个BUG都创建一…

    Linux 2023年6月6日
    0119
  • Ubuntu系统中防火墙的使用和开放端口

    sudo sudo apt-get install ufw sudo ufw status inactive: 不活跃,未开启 active:开启 sudo ufw enable …

    Linux 2023年5月27日
    081
  • rtmp和rtsp的区别

    刚刚接触到视频推流,搞不清楚rtmp和rtsp到底有什么区别 1.视频传输 RTSP+RTP主要用于IPTV,原因是传输数据使用的是UDP,在网络环境比较稳定的情况下,传输效率是比…

    Linux 2023年6月8日
    095
  • shell内置命令和外部命令的区别

    shell内置命令和外部命令的区别 内部命令实际上是shell程序的一部分,其中包含的是一些比较简单的linux系统命令,这些命令由shell程序识别并在shell程序内部完成运行…

    Linux 2023年6月7日
    078
  • bash怎么调用sql并保存结果

    mysql -uroot -pP@ssw0rd123.abc -h127.0.0.1 -P3306 -e \‘select c.TABLE_SCHEMA ,c.TABL…

    Linux 2023年6月7日
    0169
  • 附025.kubeadm部署Kubernetes更新证书

    一 查看证书 提示:由上可知,根证书有效期为10年,其他所有证书有效期为1年。 二 证书类别 由此集群根证书签发的证书有: 提示:kubelet的/var/lib/kubelet/…

    Linux 2023年6月13日
    0120
  • Python代码模板

    #!/usr/bin/env python -*- encoding: utf-8 -*- class ClassName: def __init__(self, arg1, ar…

    Linux 2023年6月14日
    088
  • 学习

    1.1、参考博客 参考的教程如下: Original: https://www.cnblogs.com/agui125/p/16032402.htmlAuthor: 风御之举Tit…

    Linux 2023年6月13日
    0101
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球