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

作者:pushiqiang 时间:2021-08-22 02:11:42 

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> # 用双下划线做跨表操作

来源:https://blog.csdn.net/pushiqiang/article/details/77621677

标签:django,自定义,主键,自增字段
0
投稿

猜你喜欢

  • 细品Dreamweaver MX 2004内建FW技术

    2010-09-02 12:38:00
  • PHP中file_get_contents函数抓取https地址出错的解决方法(两种方法)

    2023-10-14 02:31:42
  • 一篇文章弄懂PHP和HTML的嵌套写法

    2023-06-20 04:51:22
  • 实例讲解在MySQL中如何导出整个数据库

    2009-09-01 10:03:00
  • SQL Server 2005 SSIS技巧:动态目的文件名

    2008-12-05 15:47:00
  • OpenSearch 初探

    2008-06-19 12:06:00
  • Php获取金书网的书名的实现代码

    2023-07-14 02:31:21
  • XML编程实例: ASP+XML打造留言本

    2008-10-25 16:35:00
  • css元素层叠级别及z-index剖析

    2008-08-29 12:41:00
  • 如何提高网页的维护效率

    2009-03-01 11:33:00
  • golang高并发的深入理解

    2023-06-19 09:12:01
  • Python Excel处理库openpyxl详解

    2021-10-18 13:59:04
  • js“树”读取xml数据源码

    2007-08-04 19:42:00
  • python anaconda 安装 环境变量 升级 以及特殊库安装的方法

    2022-11-05 01:56:24
  • Python爬虫自动化爬取b站实时弹幕实例方法

    2023-03-21 04:45:42
  • 注册表单之死

    2008-08-07 13:02:00
  • firefox 2正则表达式

    2008-08-25 19:15:00
  • Python3.7 新特性之dataclass装饰器

    2021-05-11 13:13:40
  • 回答XML与HTML的区别

    2007-12-03 10:58:00
  • asp生成不需要数据库的中奖码

    2008-07-18 12:31:00
  • asp之家 网络编程 m.aspxhome.com