zoukankan      html  css  js  c++  java
  • 实现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表中已经插入了以下两条数据:

    那么在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字段是通过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.

    Hidden fields cannot be retrieved by name.

    If a field with the given name is not found a FieldDoesNotExist exception will be raised.

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

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

    签名:新加入开源大部队的新手程序员,拥抱开源,拥抱自由
  • 相关阅读:
    java web项目打包.war格式
    version 1.4.2-04 of the jvm is not suitable for thi
    Sugarcrm Email Integration
    sharepoint 2010 masterpage中必须的Content PlaceHolder
    微信开放平台
    Plan for caching and performance in SharePoint Server 2013
    使用自定义任务审批字段创建 SharePoint 顺序工作流
    Technical diagrams for SharePoint 2013
    To get TaskID's Integer ID value from the GUID in SharePoint workflow
    how to get sharepoint lookup value
  • 原文地址:https://www.cnblogs.com/AcAc-t/p/django_admin_model_choices_update.html
Copyright © 2011-2022 走看看