【问题标题】:What exactly is happening in this models.Manager, get_by_natural_key class/function and how can I replace it dynamically?这个models.Manager,get_by_natural_key类/函数到底发生了什么,我如何动态替换它?
【发布时间】:2019-08-12 20:09:59
【问题描述】:

我正在使用的信息来自Django documentation 和这个stackO-Question 这里的根本问题是 python,但它是 Django 特有的问题。

我正在尝试编写一些自然键以方便使用高度规范化的数据库(大量外键)。我有很多模型可以使用,所以如果我可以避免为每个模型编写一个独特的“类 ModelManager(models.Manager):”,我想这样做。

我想像这样将模型字段称为 *args:objects = ModelManager(field1,field2,field3)

class ModelManager(models.Manager):
    def get_by_natural_key(self, *args):
        list = []
        for x in args:
            list.append(x=x)    
        return self.get(list)

显然我对 Python 类的一些基本方面一无所知;我欢迎新的或关联的教育。

编辑:我编辑了类想法以包含一个 forloop。在等待启蒙时,我正在查看 python 文档。 Edit2:再想一想,我的例子的逻辑并没有正确表达我的需求。

啊哈2:谢谢Stack-O!

class ModelManager(models.Manager):
    """ ModelManager allows you to input the natural key tuple with a keyword argument of tuplecon= (x,y,z,..)
    tuplecon MUST BE A TUPLE. Keyword argument of tuplecon2 = (x=x, y=y, z=z)"""
    def __init__(self, tuplecon, tuplecon2):
        self.tuplecon = tuplecon
        self.tuplecon2 = tuplecon2

    def get_by_natural_key(self, *tuplecon):

        return self.get(*(self.tuplecon2))

【问题讨论】:

    标签: python-3.x django-models


    【解决方案1】:

    啊哈!我找到了答案。它比我想象的要复杂得多,但这里是:django-drynk

    Decorators for Django models
    """
    from functools import reduce
    from inspect import signature, Parameter
    
    from django.core.exceptions import ImproperlyConfigured
    from django.db import models
    
    
    def with_natural_key(fields):
        """
        Decorator to add DRY natural key support to a Django model.
    
        Adds a Manager class with get_by_natural_key and a corresponding natural_key
        method on the model.
        """
        assert len(fields) > 0
        assert 'self' not in fields # for NaturalKeyManager
    
        def natural_key_wrapper(klass):
            def _natural_key_field(self, field_name):
                if isinstance(self._meta.get_field(field_name), models.fields.related.ForeignKey):
                    real_field = getattr(self, self._meta.get_field(field_name).name)
                    if getattr(real_field, 'natural_key', None):
                        return real_field.natural_key()
                    else:
                        # if the foreign model can't provide a natural key, then the
                        # auto-generated get_by_natural_key won't work
                        raise ImproperlyConfigured("expected to find a `natural_key` method "
                                                   "on {}".format(field_name))
                return (self.__dict__[field_name], )
    
            def _natural_key(self):
                return reduce(lambda x, y: x + y, [self._natural_key_field(x) for x in fields])
    
            klass._natural_key_field = _natural_key_field
            klass.natural_key = _natural_key
            klass.natural_key.__name__ = "natural_key"
            klass.natural_key.__doc__ = "Return a natural key for the model: ({})".format(
                ", ".join(fields))
    
            # determine and record dependencies
            # also, unroll dependencies and read their get_by_natural_key signatures
            dependencies = []
            unrolled_fields = []
    
            for field_name in fields:
                if isinstance(klass._meta.get_field(field_name), models.fields.related.ForeignKey):
                    try:
                        # Django 1.9
                        to_model = klass._meta.get_field(field_name).remote_field.to
                    except AttributeError:
                        # Django 1.8
                        to_model = klass._meta.get_field(field_name).related_field.model
                    dependencies.append(_build_dependency(to_model, field_name))
                    unrolled_fields.extend(_unroll_natural_key(to_model, field_name))
                else:
                    unrolled_fields.append(field_name)
    
            if len(dependencies) > 0:
                klass.natural_key.dependencies = dependencies
    
    
            class NaturalKeyManager(models.Manager):
                """
                Implements get_by_natural_key for {}
                """
                def get_by_natural_key(self, *args):
                    """
                    Find an object by its natural key
                    """
                    if len(args) != len(unrolled_fields):
                        raise RuntimeError("expected {} arguments ({}), got {}".format(
                            len(unrolled_fields), ", ".join(unrolled_fields), len(args)
                        ))
    
                    return self.get(**dict(zip(unrolled_fields, args)))
    
            NaturalKeyManager.__name__ = klass.__name__ + "NaturalKeyManager"
            NaturalKeyManager.__doc__ = NaturalKeyManager.__doc__.format(klass.__name__)
    
            # fix up the signature to show the expected positional parameters instead of "*args"
            # this cleans up help() but also allows later models to use this in constructing
            # their get_by_natural_key method
            sig = signature(NaturalKeyManager.get_by_natural_key)
            sig = sig.replace(parameters=[Parameter(f, Parameter.POSITIONAL_ONLY) for f in ['self'] + unrolled_fields])
            setattr(NaturalKeyManager.get_by_natural_key, "__signature__", sig)
    
            _m = NaturalKeyManager()
            # opinionated choice here: this should be the default manager and be called `objects`
            _m.contribute_to_class(klass, "objects")
    
            return klass
    
        return natural_key_wrapper
    
    
    def _build_dependency(model, field_name):
        app_name = model._meta.app_config.label
        model_name = model.__name__
        return "{}.{}".format(app_name, model_name)
    
    
    def _unroll_natural_key(model, field_name):
        try:
            foreign_gbnk = model.objects.get_by_natural_key
        except AttributeError as e:
            raise ImproperlyConfigured("expected to find a manager called "
                                       "`objects` with a `get_by_natural_key` method") from e
        sig = signature(foreign_gbnk)
        return ["{}__{}".format(field_name, k) for k,v in sig.parameters.items()]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-29
      • 2021-03-18
      • 2014-01-25
      • 1970-01-01
      相关资源
      最近更新 更多