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对列表排序的方法实例分析
May 16 Python
python 实现删除文件或文件夹实例详解
Dec 04 Python
Python将多个excel文件合并为一个文件
Jan 03 Python
python主线程捕获子线程的方法
Jun 17 Python
详解python的argpare和click模块小结
Mar 31 Python
Python3安装Pillow与PIL的方法
Apr 03 Python
Python符号计算之实现函数极限的方法
Jul 15 Python
画pytorch模型图,以及参数计算的方法
Aug 17 Python
Python3 搭建Qt5 环境的方法示例
Jul 16 Python
matplotlib.pyplot.plot()参数使用详解
Jul 28 Python
Python用access判断文件是否被占用的实例方法
Dec 17 Python
Python可视化神器pyecharts之绘制箱形图
Jul 07 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读取文件并可支持远程文件的代码分享
2012/10/03 PHP
PHP基于cookie与session统计网站访问量并输出显示的方法
2016/01/15 PHP
学习thinkphp5.0验证类使用方法
2017/11/16 PHP
Javascript级联下拉菜单以及AJAX数据验证核心代码
2013/05/10 Javascript
使用Jquery实现点击文字后变成文本框且可修改
2013/09/21 Javascript
jquery上传插件fineuploader上传文件使用方法(jquery图片上传插件)
2013/12/05 Javascript
创建、调用JavaScript对象的方法集锦
2014/12/24 Javascript
js实现分享到随页面滚动而滑动效果的方法
2015/04/10 Javascript
详解A标签中href=&quot;&quot;的几种用法
2017/08/20 Javascript
Node 升级到最新稳定版的方法分享
2018/05/17 Javascript
JS实现根据指定值删除数组中的元素操作示例
2018/08/02 Javascript
微信小程序实现搜索历史功能
2020/03/26 Javascript
python中的yield使用方法
2014/02/11 Python
python实现获取序列中最小的几个元素
2014/09/25 Python
python生成tensorflow输入输出的图像格式的方法
2018/02/12 Python
python 执行shell命令并将结果保存的实例
2018/05/11 Python
Numpy数据类型转换astype,dtype的方法
2018/06/09 Python
Django MEDIA的配置及用法详解
2019/07/25 Python
关于python导入模块import与常见的模块详解
2019/08/28 Python
python 求定积分和不定积分示例
2019/11/20 Python
Pytorch在dataloader类中设置shuffle的随机数种子方式
2020/01/14 Python
Casetify官网:自制专属手机壳、iPad护壳和Apple Watch手表带
2018/05/09 全球购物
美国女士时尚珠宝及配饰购物网站:Icing
2018/07/02 全球购物
企业演讲稿范文
2013/12/28 职场文书
幼儿园新学期寄语
2014/01/18 职场文书
小学生读书感言
2014/02/12 职场文书
餐厅采购员岗位职责
2014/03/06 职场文书
取保候审保证书
2014/04/30 职场文书
社区反邪教工作方案
2014/06/16 职场文书
2014年教师个人工作总结
2014/11/10 职场文书
2015年社区民政工作总结
2015/04/21 职场文书
2015年司机年终工作总结
2015/05/14 职场文书
学生会主席任命书
2015/09/21 职场文书
2016秋季小学开学寄语
2015/12/03 职场文书
pytorch model.cuda()花费时间很长的解决
2021/06/01 Python
Python使用华为API为图像设置多个锚点标签
2022/04/12 Python