django自定义非主键自增字段类型详解(auto increment field)


Posted in Python onMarch 30, 2020

1.django自定义字段类型,实现非主键字段的自增

# -*- encoding: utf-8 -*-

from django.db.models.fields import Field, IntegerField
from django.core import checks, exceptions
from django.utils.translation import ugettext_lazy as _


class AutoIncreField(Field):
 description = _("Integer")

 empty_strings_allowed = False
 default_error_messages = {
 'invalid': _("'%(value)s' value must be an integer."),
 }

 def __init__(self, *args, **kwargs):
 kwargs['blank'] = True
 super(AutoIncreField, self).__init__(*args, **kwargs)

 def check(self, **kwargs):
 errors = super(AutoIncreField, self).check(**kwargs)
 # 每张表只能设置一个字段为自增长字段,这个字段可以是主键,也可以不是主键,如果不是主键,则必须设置为一种“键(key)”
 # (primary key)也是键(key)的一种,key还包括外键(foreign key)、唯一键(unique key)
 errors.extend(self._check_key())
 return errors

 def _check_key(self):
 if not self.unique:
  return [
  checks.Error(
   'AutoIncreFields must set key(unique=True).',
   obj=self,
   id='fields.E100',
  ),
  ]
 else:
  return []

 def deconstruct(self):
 name, path, args, kwargs = super(AutoIncreField, self).deconstruct()
 del kwargs['blank']
 kwargs['unique'] = True
 return name, path, args, kwargs

 def get_internal_type(self):
 return "AutoIncreField"

 def to_python(self, value):
 if value is None:
  return value
 try:
  return int(value)
 except (TypeError, ValueError):
  raise exceptions.ValidationError(
  self.error_messages['invalid'],
  code='invalid',
  params={'value': value},
  )

 def db_type(self, connection):
 return 'bigint AUTO_INCREMENT'

 def rel_db_type(self, connection):
 return IntegerField().db_type(connection=connection)

 def validate(self, value, model_instance):
 pass

 def get_db_prep_value(self, value, connection, prepared=False):
 if not prepared:
  value = self.get_prep_value(value)
  value = connection.ops.validate_autopk_value(value)
 return value

 def get_prep_value(self, value):
 value = super(AutoIncreField, self).get_prep_value(value)
 if value is None:
  return None
 return int(value)

 def contribute_to_class(self, cls, name, **kwargs):
 assert not cls._meta.auto_field, "A model can't have more than one AutoIncreField."
 super(AutoIncreField, self).contribute_to_class(cls, name, **kwargs)
 cls._meta.auto_field = self

 def formfield(self, **kwargs):
 return None

2.使用

class Test(models.Model):

 id = models.UUIDField(primary_key=True, default=uuid4)
 numbering = AutoIncreField(_(u'numbering'), unique=True)
 name = models.CharField(_(u'name'), max_length=32, blank=True, null=True)

3.bug

当save()后并不能刷新instance,及save后numbering会为空值,需要重写get一次.

如果您修复了这个问题请留言回复下,谢谢

4.bug修复

以一种非常不优雅的方法进行了简单修复,重写了模型的save方法,在save后从新get

class AutoIncreFieldFixMinxin(object):
 def save(self, *args, **kwargs):
 super(AutoIncreFieldFixMinxin, self).save(*args, **kwargs)
 auto_field = self._meta.auto_field.name
 new_obj = self.__class__.objects.get(pk=self.pk)
 setattr(self, auto_field, int(getattr(new_obj, auto_field)))


class Test(AutoIncreFieldFixMinxin, models.Model):
 id = models.UUIDField(primary_key=True, default=uuid4)
 sequence = AutoIncreField(_(u'sequence'), unique=True)
 name = models.CharField(_(u'name'), max_length=100)

补充知识:Django model 表与表的关系

一对多:models.ForeignKey(其他表)

多对多:models.ManyToManyField(其他表)

一对一:models.OneToOneField(其他表)

应用场景:

一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)

例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。

多对多:在某表中创建一行数据是,有一个可以多选的下拉框

例如:创建用户信息,需要为用户指定多个爱好

一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了

例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据

ForeignKey(ForeignObject) # ForeignObject(RelatedField)
 to,    # 要进行关联的表名
 to_field=None,  # 要关联的表中的字段名称
 on_delete=None,  # 当删除关联表中的数据时,当前表与其关联的行的行为
     - models.CASCADE,删除关联数据,与之关联也删除
     - models.DO_NOTHING,删除关联数据,引发错误IntegrityError
     - models.PROTECT,删除关联数据,引发错误ProtectedError
     - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
     - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
     - models.SET,删除关联数据,
       a. 与之关联的值设置为指定值,设置:models.SET(值)
       b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

       def func():
        return 10

       class MyModel(models.Model):
        user = models.ForeignKey(
        to="User",
        to_field="id"
        on_delete=models.SET(func),)
 related_name=None,  # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
 related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
 limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
     # 如:
      - limit_choices_to={'nid__gt': 5}
      - limit_choices_to=lambda : {'nid__gt': 5}

      from django.db.models import Q
      - limit_choices_to=Q(nid__gt=10)
      - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
      - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
 db_constraint=True  # 是否在数据库中创建外键约束
 parent_link=False  # 在Admin中是否显示关联数据


 OneToOneField(ForeignKey)
 to,    # 要进行关联的表名
 to_field=None  # 要关联的表中的字段名称
 on_delete=None,  # 当删除关联表中的数据时,当前表与其关联的行的行为

     ###### 对于一对一 ######
     # 1. 一对一其实就是 一对多 + 唯一索引
     # 2.当两个类之间有继承关系时,默认会创建一个一对一字段
     # 如下会在A表中额外增加一个c_ptr_id列且唯一:
      class C(models.Model):
      nid = models.AutoField(primary_key=True)
      part = models.CharField(max_length=12)

      class A(C):
      id = models.AutoField(primary_key=True)
      code = models.CharField(max_length=1)

 ManyToManyField(RelatedField)
 to,    # 要进行关联的表名
 related_name=None,  # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
 related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
 limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
     # 如:
      - limit_choices_to={'nid__gt': 5}
      - limit_choices_to=lambda : {'nid__gt': 5}

      from django.db.models import Q
      - limit_choices_to=Q(nid__gt=10)
      - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
      - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
 symmetrical=None,  # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
     # 做如下操作时,不同的symmetrical会有不同的可选字段
     models.BB.objects.filter(...)

     # 可选字段有:code, id, m1
      class BB(models.Model):

      code = models.CharField(max_length=12)
      m1 = models.ManyToManyField('self',symmetrical=True)

     # 可选字段有: bb, code, id, m1
      class BB(models.Model):

      code = models.CharField(max_length=12)
      m1 = models.ManyToManyField('self',symmetrical=False)

 through=None,  # 自定义第三张表时,使用字段用于指定关系表
 through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
     from django.db import models

     class Person(models.Model):
      name = models.CharField(max_length=50)

     class Group(models.Model):
      name = models.CharField(max_length=128)
      members = models.ManyToManyField(
      Person,
      through='Membership',
      through_fields=('group', 'person'),
      )

     class Membership(models.Model):
      group = models.ForeignKey(Group, on_delete=models.CASCADE)
      person = models.ForeignKey(Person, on_delete=models.CASCADE)
      inviter = models.ForeignKey(
      Person,
      on_delete=models.CASCADE,
      related_name="membership_invites",
      )
      invite_reason = models.CharField(max_length=64)
 db_constraint=True,  # 是否在数据库中创建外键约束
 db_table=None,  # 默认创建第三张表时,数据库中表的名称

ForeignKey外键(跨表操作):

跨表操作1

v = models.Host.objects.filter(nid__gt=0)

v[0].b.caption #通过.进行跨表操作,在对象中去做跨表操作用.

跨表操作2

v = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption') #使用values()取值时可以用双下划线做跨表操作

for row in v:

print(row['nid'],row['hostname'],row['b_id'],row['b__caption'])

前端:

<td>{{ row.b__caption }}</td> # 用双下划线做跨表操作

以上这篇django自定义非主键自增字段类型详解(auto increment field)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python实现命令行通讯录实例教程
Aug 18 Python
Python中的sort()方法使用基础教程
Jan 08 Python
Python3.5 Pandas模块之DataFrame用法实例分析
Apr 23 Python
使用Python的OpenCV模块识别滑动验证码的缺口(推荐)
May 10 Python
Mac安装python3的方法步骤
Aug 09 Python
Python中的 ansible 动态Inventory 脚本
Jan 19 Python
python smtplib发送多个email联系人的实现
Oct 09 Python
Python中Permission denied的解决方案
Apr 02 Python
完美处理python与anaconda环境变量的冲突问题
Apr 07 Python
Python实战之用tkinter库做一个鼠标模拟点击器
Apr 27 Python
浅谈Python数学建模之数据导入
Jun 23 Python
python读取并查看npz/npy文件数据以及数据显示方法
Apr 14 Python
Python GUI编程学习笔记之tkinter事件绑定操作详解
Mar 30 #Python
VSCode基础使用与VSCode调试python程序入门的图文教程
Mar 30 #Python
Python实现Wordcloud生成词云图的示例
Mar 30 #Python
Django ModelForm操作及验证方式
Mar 30 #Python
windows10环境下用anaconda和VScode配置的图文教程
Mar 30 #Python
Python GUI编程学习笔记之tkinter控件的介绍及基本使用方法详解
Mar 30 #Python
Python GUI编程学习笔记之tkinter界面布局显示详解
Mar 30 #Python
You might like
PHP开发入门教程之面向对象
2006/12/05 PHP
Discuz!下Memcache缓存实现方法
2010/05/28 PHP
php截取后台登陆密码的代码
2012/05/05 PHP
深入解析PHP中逗号与点号的区别
2013/08/05 PHP
php foreach正序倒序输出示例代码
2014/07/01 PHP
destoon实现会员商铺中指定会员或会员组投放广告的方法
2014/08/21 PHP
编写PHP脚本清除WordPress头部冗余代码的方法讲解
2016/03/01 PHP
Thinkphp微信公众号支付接口
2016/08/04 PHP
yii2安装详细流程
2018/05/23 PHP
PHP常见字符串操作函数与用法总结
2019/03/04 PHP
检测是否已安装 .NET Framework 3.5的js脚本
2009/02/14 Javascript
为JavaScript提供睡眠功能(sleep) 自编译JS引擎
2010/08/16 Javascript
jQuery实用基础超详细介绍
2013/04/11 Javascript
将文本输入框内容加入表中的js代码
2013/08/18 Javascript
轮播的简单实现方法
2016/07/28 Javascript
AngularJs directive详解及示例代码
2016/09/01 Javascript
浅谈JS中的三种字符串连接方式及其性能比较
2016/09/02 Javascript
深入理解JavaScript中的预解析
2017/01/04 Javascript
函数四种调用模式以及其中的this指向
2017/01/16 Javascript
bootstrap模态框示例代码分享
2017/05/17 Javascript
AngularJS模糊查询功能实现代码(过滤内容下拉菜单排序过滤敏感字符验证判断后添加表格信息)
2017/10/24 Javascript
详解TypeScript+Vue 插件 vue-class-component的使用总结
2019/02/18 Javascript
webpack proxy 使用(代理的使用)
2020/01/10 Javascript
[51:36]Optic vs Newbee 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
python显示生日是星期几的方法
2015/05/27 Python
Django项目中用JS实现加载子页面并传值的方法
2018/05/28 Python
关于Python内存分配时的小秘密分享
2019/09/05 Python
python实现指定ip端口扫描方式
2019/12/17 Python
将pymysql获取到的数据类型是tuple转化为pandas方式
2020/05/15 Python
Python定时任务APScheduler原理及实例解析
2020/05/30 Python
读取nii或nii.gz文件中的信息即输出图像操作
2020/07/01 Python
美国顶级防滑鞋:Shoes For Crews
2017/03/27 全球购物
塑料制成的可水洗的编织平底鞋和鞋子:Rothy’s
2018/09/16 全球购物
简历中的自我评价怎么写
2014/01/29 职场文书
2015年中秋晚会主持词
2015/07/01 职场文书
canvas 中如何实现物体的框选
2022/08/05 Javascript