Created
May 30, 2019 08:29
-
-
Save codetalks-new/dc79f6055031aea154b91a02b27cbed2 to your computer and use it in GitHub Desktop.
Model 保存之后第延迟初始化的 CharField 字段实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class BasicDeferredDefaultAttribute: | |
""" | |
实现支持延迟的 deferred_default 方法 | |
在之前的 Video及Post, asset_id 相关的默认属性生成需要用到 pk, pk 是数据库保存之后自动生成的. | |
本 Descriptor 实现逻辑是相当于修改版本的 DeferredAttribute. 如果有 pk 并且对应的值为空值.则调用对应的函数读取值并设置. | |
然后再调用 save. | |
这个方法要求 Model 在 init 中设置对应的值为 models.DEFERRED | |
```py | |
def __init__(*args,**kwargs): | |
kwargs.setdefault('<字段名>', models.DEFERRED) | |
``` | |
另一个实现上面逻辑的思路是使用 `post_save`, `post_save` 的一个小问题是,如果调用 `bulk_create` 创建的话, | |
`save` 及相关的 `post_save`. 不会调用. | |
参考 django.db.models.query_utils.DeferredAttribute | |
""" | |
def __init__(self, *, field, deferred_default: Callable[[models.Model], any]): | |
self.field_name = field.name | |
self.field = field | |
self.deferred_default = deferred_default | |
def __get__(self, instance, cls=None): | |
if instance is None: | |
return self | |
data: dict = instance.__dict__ | |
old_val = data.get(self.field_name) | |
if old_val: | |
return old_val | |
if not instance.pk: | |
if old_val is None: | |
return self.field.get_default() | |
return old_val | |
val = self.deferred_default(instance) | |
assert val | |
data[self.field_name] = val | |
instance.save(update_fields=[self.field_name]) | |
return val | |
class BasicDeferredDefaultFieldMixin: | |
"""模型属性实现为 DeferredDefaultAttribute 的字段 Mixin, 只支持简单的标量字段 """ | |
def __init__(self, *args, **kwargs): | |
deferred_default = kwargs.pop("deferred_default", None) | |
if not (deferred_default and callable(deferred_default)): | |
raise ValueError("deferred_default 不能为空并必须是可调用的") | |
self.deferred_default = deferred_default | |
super(BasicDeferredDefaultFieldMixin, self).__init__(*args, **kwargs) | |
def deconstruct(self): | |
name, path, args, kwargs = super( | |
BasicDeferredDefaultFieldMixin, self | |
).deconstruct() | |
kwargs["deferred_default"] = self.deferred_default | |
return name, path, args, kwargs | |
def contribute_to_class(self, cls, name, **kwargs): | |
super(BasicDeferredDefaultFieldMixin, self).contribute_to_class( | |
cls, name, **kwargs | |
) | |
setattr( | |
cls, | |
name, | |
BasicDeferredDefaultAttribute( | |
field=self, deferred_default=self.deferred_default | |
), | |
) | |
class DeferredCharField(BasicDeferredDefaultFieldMixin, models.CharField): | |
"""相关文档说明参考 BasicDeferredDefaultAttribute """ | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment