django之关联field 描述子是如何实现的

Posted on 2018-09-08 22:17  王将军之武库  阅读(333)  评论(0编辑  收藏  举报

model定义时,每个field都是一个类属性,一个对象。在生成类时,属性有contribute_to_class的方法,会调用该方法。

m2m field,它会先调用自己的contribute_to_class,然后调用父类的contribute_to_class,因为m2m类是继承自relatfield,

def contribute_to_class(self, cls, name, virtual_only=False):

        super(RelatedField, self).contribute_to_class(cls, name, virtual_only=virtual_only)#在option添加一个field

        self.opts = cls._meta

        if not cls._meta.abstract:
            if self.remote_field.related_name:
                related_name = force_text(self.remote_field.related_name) % {
                    'class': cls.__name__.lower(),
                    'app_label': cls._meta.app_label.lower()
                }
                self.remote_field.related_name = related_name

            def resolve_related_class(model, related, field):
                field.remote_field.model = related
                field.do_related_class(related, model)
            lazy_related_operation(resolve_related_class, cls, self.remote_field.model, field=self)

def do_related_class(self, other, cls):#关键是调用这个函数时把两个model类调换了,model与related位置对调。
        self.set_attributes_from_rel()
        self.contribute_to_related_class(other, self.remote_field)

def lazy_related_operation(function, model, *related_models, **kwargs):
    """
    Schedule `function` to be called once `model` and all `related_models`
    have been imported and registered with the app registry. `function` will
    be called with the newly-loaded model classes as its positional arguments,
    plus any optional keyword arguments.

    The `model` argument must be a model class. Each subsequent positional
    argument is another model, or a reference to another model - see
    `resolve_relation()` for the various forms these may take. Any relative
    references will be resolved relative to `model`.

    This is a convenience wrapper for `Apps.lazy_model_operation` - the app
    registry model used is the one found in `model._meta.apps`.
    """
    models = [model] + [resolve_relation(model, rel) for rel in related_models]
    model_keys = (make_model_tuple(m) for m in models)
    apps = model._meta.apps
    return apps.lazy_model_operation(partial(function, **kwargs), *model_keys)

def lazy_model_operation(self, function, *model_keys):
        """
        Take a function and a number of ("app_label", "modelname") tuples, and
        when all the corresponding models have been imported and registered,
        call the function with the model classes as its arguments.

        The function passed to this method must accept exactly n models as
        arguments, where n=len(model_keys).
        """
        # If this function depends on more than one model, we recursively turn
        # it into a chain of functions that accept a single model argument and
        # pass each in turn to lazy_model_operation.
        model_key, more_models = model_keys[0], model_keys[1:]
        if more_models:
            supplied_fn = function

            def function(model):
                next_function = partial(supplied_fn, model)
                # Annotate the function with its field for retrieval in
                # migrations.state.StateApps.
                if getattr(supplied_fn, 'keywords', None):
                    next_function.field = supplied_fn.keywords.get('field')
                self.lazy_model_operation(next_function, *more_models)

        # If the model is already loaded, pass it to the function immediately.
        # Otherwise, delay execution until the class is prepared.
        try:
            model_class = self.get_registered_model(*model_key)
        except LookupError:
            self._pending_operations[model_key].append(function)
        else:
            function(model_class)

 

Copyright © 2024 王将军之武库
Powered by .NET 9.0 on Kubernetes