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监控主机是否存活并以邮件报警
Sep 22 Python
python线程中同步锁详解
Apr 27 Python
Python单元测试实例详解
May 25 Python
使用python画个小猪佩奇的示例代码
Jun 06 Python
Python变量类型知识点总结
Feb 18 Python
计算机二级python学习教程(2) python语言基本语法元素
May 16 Python
Python中使用双下划线防止类属性被覆盖问题
Jun 27 Python
详解Python是如何实现issubclass的
Jul 24 Python
opencv3/C++实现视频背景去除建模(BSM)
Dec 11 Python
浅谈Python3中print函数的换行
Aug 05 Python
python3美化表格数据输出结果的实现代码
Apr 14 Python
Python中else的三种使用场景
Jun 16 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生成plist数据的方法
2015/06/16 PHP
orm获取关联表里的属性值
2016/04/17 PHP
javascript应用:Iframe自适应其加载的内容高度
2007/04/10 Javascript
JQuery 无废话系列教程(二) jquery实战篇上
2009/06/23 Javascript
Javascript 修改String 对象 增加去除空格功能(示例代码)
2013/11/30 Javascript
jQuery中:file选择器用法实例
2015/01/04 Javascript
nodejs爬虫抓取数据乱码问题总结
2015/07/03 NodeJs
跟我学习javascript的this关键字
2020/05/28 Javascript
Node.js学习入门
2017/01/03 Javascript
Vue监听数据对象变化源码
2017/03/09 Javascript
Angularjs 动态添加指令并绑定事件的方法
2017/04/13 Javascript
layui弹出层效果实现代码
2017/05/19 Javascript
AngularJS 中ui-view传参的实例详解
2017/08/25 Javascript
React数据传递之组件内部通信的方法
2017/12/31 Javascript
解决Vue开发中对话框被遮罩层挡住的问题
2018/11/26 Javascript
基于vue实现一个禅道主页拖拽效果
2019/05/27 Javascript
python实现给字典添加条目的方法
2014/09/25 Python
python3批量删除豆瓣分组下的好友的实现代码
2016/06/07 Python
利用Python获取操作系统信息实例
2016/09/02 Python
用Python3创建httpServer的简单方法
2018/06/04 Python
python十进制和二进制的转换方法(含浮点数)
2018/07/07 Python
Python动态生成多维数组的方法示例
2018/08/09 Python
python图形工具turtle绘制国际象棋棋盘
2019/05/23 Python
Python+OpenCV图像处理——实现轮廓发现
2020/10/23 Python
python使用dlib进行人脸检测和关键点的示例
2020/12/05 Python
Orvis官网:自1856年以来,优质服装、飞钓装备等
2018/12/17 全球购物
戴尔新西兰官网:Dell New Zealand
2020/01/07 全球购物
某科技软件测试面试题
2013/05/19 面试题
学期自我鉴定
2013/11/04 职场文书
校园安全演讲稿
2014/05/09 职场文书
六一儿童节演讲稿
2014/05/23 职场文书
酒店管理毕业生自荐信
2014/05/25 职场文书
幼儿园小班教师个人工作总结
2015/02/06 职场文书
教务处教学工作总结
2015/08/10 职场文书
大学生村官驻村工作心得体会
2016/01/23 职场文书
使用Bandicam录制鼠标指针并附带点击声音,还可以添加点击动画效果
2022/04/11 数码科技