114.django复制模型对象和自定义字段

1.复制模型对象

产生原因:项目增加某部分表数据的复制功能,但是很难受由于项目是初始阶段,表中字段一直变化,通过对象复制操作
每次增加一些字段我就需要改代码,特别烦人,所以产生了这个东西.

代码如下很简单:
from django.db.models import AutoField, DateTimeField
from PublicMethod.CustomDjangoModelFiled import AutoIncreField   # 这个字段是我自定义的一个自增类型字段(google可以找到)
def copy_object(obj):
    """
    拷贝模型对象对象,并排除exclude中的字段,返回一个新的对象
    :param obj 表示要copy的对象
    :param exclude 表示哪些字段排除之外
    :return 返回一个新的对象
    """
    # 关于其中排除字段的原因AutoField和AutoIncreField由于是自增字段所以该字段对应的值不应该复制过来,
DateTimeField字段也是auto_now=True所以排除掉
    initial = dict([(f.name, getattr(obj, f.name))for f in obj._meta.fields
                    if not (isinstance(f, AutoField) or isinstance(f, DateTimeField) or isinstance(f, AutoIncreField))
                    and not f in obj._meta.parents.values()])
    return obj.__class__(**initial)

2.自定义自增字段

产生原因:我们的项目我来公司之前的表没有设置id字段,使用Char类型的字段当做主键的,但是后期项目需要我给所有的表添加自增的id
字段,但是如果你在django里面使用AutoField那么primary_key=True是必须的,而现在我只需要一个自增的主键id,所以通过google和
看AutoField的源码拼出来了一个新字段.

建议:每个表设置主键id字段或者一个唯一的字段,此字段建议不要出现中文(项目中使用了的基本后期全部改掉了,所以不要尝试)使用自
增字段或者使用uuid(推荐使用:因为是根据时间戳生成的所以基本不存在重复性问题),同时建议加上updated_time以及created_time
字段方便后期查询过滤使用如下即可(推荐使用继承方式将下面三个字段写入父类中,通过其他模型继承,某写表不需要是可以不继承,同时
建议将id字段和时间字段分开两个父类):
    id = models.AutoField(primary_key=True, verbose_name="ID")
    update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")


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):
    def __init__(self, *args, **kwargs):
        kwargs["blank"] = True
        super(AutoIncreField, self).__init__(*args, **kwargs)
        
    empty_strings_allowed = False
    default_error_messages = {
        'invalid': _('“%(value)s” value must be an integer.'),
    }
    description = _("Integer")

    def check(self, **kwargs):
        return [
            *super().check(**kwargs),
            *self._check_key(),
        ]

    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 validate(self, value, model_instance):
        pass

    def get_prep_value(self, value):
        value = super().get_prep_value(value)
        if value is None:
            return None
        try:
            return int(value)
        except (TypeError, ValueError) as e:
            raise e.__class__(
                "Field '%s' expected a number but got %r." % (self.name, value),
            ) from e

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

    def get_internal_type(self):
        # return "AutoIncreField"  # 请注意这里,不要随便起名字,这个就是我自己起的,但是迁移的时候这个字段迁移不到数据库中,
        # 你创建的字段类型和已经存在的哪个相似,就写那个比如我的和AutoField类似,就写这个,否则找不到错误在哪里除非你自己看源码
        return "AutoField"

    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 rel_db_type(self, connection):
        return IntegerField().db_type(connection=connection)

    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 contribute_to_class(self, cls, name, **kwargs):
        assert not cls._meta.auto_field, (
            "Model %s can't have more than one auto-generated field."
            % cls._meta.label
        )
        super().contribute_to_class(cls, name, **kwargs)
        cls._meta.auto_field = self

    def formfield(self, **kwargs):
        return None
原文地址:https://www.cnblogs.com/liuzhanghao/p/14028576.html