<s> from __future__ import unicode_literals <EOL> import django <EOL> from django . core import checks <EOL> from django . db import IntegrityError , router <EOL> from django . db . models . fields . related import ForeignKey <EOL> from django . utils . functional import cached_property <EOL> try : <EOL> from django . db . models . fields . related import ReverseManyToOneDescriptor <EOL> except ImportError : <EOL> from django . db . models . fields . related import ForeignRelatedObjectsDescriptor as ReverseManyToOneDescriptor <EOL> from modelcluster . utils import sort_by_fields <EOL> from modelcluster . queryset import FakeQuerySet <EOL> from modelcluster . models import ClusterableModel <EOL> def create_deferring_foreign_related_manager ( related , original_manager_cls ) : <EOL> """<STR_LIT>""" <EOL> relation_name = related . get_accessor_name ( ) <EOL> rel_field = related . field <EOL> rel_model = related . related_model <EOL> superclass = rel_model . _default_manager . __class__ <EOL> class DeferringRelatedManager ( superclass ) : <EOL> def __init__ ( self , instance ) : <EOL> super ( DeferringRelatedManager , self ) . __init__ ( ) <EOL> self . model = rel_model <EOL> self . instance = instance <EOL> def get_live_query_set ( self ) : <EOL> return self . get_live_queryset ( ) <EOL> def get_live_queryset ( self ) : <EOL> """<STR_LIT>""" <EOL> return original_manager_cls ( self . instance ) . get_queryset ( ) <EOL> def get_queryset ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> results = self . instance . _cluster_related_objects [ relation_name ] <EOL> except ( AttributeError , KeyError ) : <EOL> return self . get_live_queryset ( ) <EOL> return FakeQuerySet ( related . related_model , results ) <EOL> def get_prefetch_queryset ( self , instances , queryset = None ) : <EOL> if queryset is None : <EOL> db = self . _db or router . db_for_read ( self . model , instance = instances [ <NUM_LIT:0> ] ) <EOL> queryset = super ( DeferringRelatedManager , self ) . get_queryset ( ) . using ( db ) <EOL> rel_obj_attr = rel_field . get_local_related_value <EOL> instance_attr = rel_field . get_foreign_related_value <EOL> instances_dict = dict ( ( instance_attr ( inst ) , inst ) for inst in instances ) <EOL> query = { '<STR_LIT>' % rel_field . name : instances } <EOL> qs = queryset . filter ( ** query ) <EOL> for rel_obj in qs : <EOL> instance = instances_dict [ rel_obj_attr ( rel_obj ) ] <EOL> setattr ( rel_obj , rel_field . name , instance ) <EOL> cache_name = rel_field . related_query_name ( ) <EOL> return qs , rel_obj_attr , instance_attr , False , cache_name <EOL> def get_object_list ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> cluster_related_objects = self . instance . _cluster_related_objects <EOL> except AttributeError : <EOL> cluster_related_objects = { } <EOL> self . instance . _cluster_related_objects = cluster_related_objects <EOL> try : <EOL> object_list = cluster_related_objects [ relation_name ] <EOL> except KeyError : <EOL> object_list = list ( self . get_live_queryset ( ) ) <EOL> cluster_related_objects [ relation_name ] = object_list <EOL> return object_list <EOL> def add ( self , * new_items ) : <EOL> """<STR_LIT>""" <EOL> items = self . get_object_list ( ) <EOL> def items_match ( item , target ) : <EOL> return ( item is target ) or ( item . pk == target . pk and item . pk is not None ) <EOL> for target in new_items : <EOL> item_matched = False <EOL> for i , item in enumerate ( items ) : <EOL> if items_match ( item , target ) : <EOL> items [ i ] = target <EOL> item_matched = True <EOL> break <EOL> if not item_matched : <EOL> items . append ( target ) <EOL> setattr ( target , related . field . name , self . instance ) <EOL> if rel_model . _meta . ordering and len ( items ) > <NUM_LIT:1> : <EOL> sort_by_fields ( items , rel_model . _meta . ordering ) <EOL> def remove ( self , * items_to_remove ) : <EOL> """<STR_LIT>""" <EOL> items = self . get_object_list ( ) <EOL> def items_match ( item , target ) : <EOL> return ( item is target ) or ( item . pk == target . pk and item . pk is not None ) <EOL> for target in items_to_remove : <EOL> items [ : ] = [ item for item in items if not items_match ( item , target ) ] <EOL> def create ( self , ** kwargs ) : <EOL> items = self . get_object_list ( ) <EOL> new_item = related . related_model ( ** kwargs ) <EOL> items . append ( new_item ) <EOL> return new_item <EOL> def clear ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> cluster_related_objects = self . instance . _cluster_related_objects <EOL> except AttributeError : <EOL> cluster_related_objects = { } <EOL> self . instance . _cluster_related_objects = cluster_related_objects <EOL> cluster_related_objects [ relation_name ] = [ ] <EOL> def commit ( self ) : <EOL> """<STR_LIT>""" <EOL> if not self . instance . pk : <EOL> raise IntegrityError ( "<STR_LIT>" % relation_name ) <EOL> try : <EOL> final_items = self . instance . _cluster_related_objects [ relation_name ] <EOL> except ( AttributeError , KeyError ) : <EOL> return <EOL> original_manager = original_manager_cls ( self . instance ) <EOL> live_items = list ( original_manager . get_queryset ( ) ) <EOL> for item in live_items : <EOL> if item not in final_items : <EOL> item . delete ( ) <EOL> for item in final_items : <EOL> if django . VERSION >= ( <NUM_LIT:1> , <NUM_LIT:9> ) : <EOL> original_manager . add ( item , bulk = False ) <EOL> else : <EOL> original_manager . add ( item ) <EOL> del self . instance . _cluster_related_objects [ relation_name ] <EOL> return DeferringRelatedManager <EOL> class ChildObjectsDescriptor ( ReverseManyToOneDescriptor ) : <EOL> def __get__ ( self , instance , instance_type = None ) : <EOL> if instance is None : <EOL> return self <EOL> return self . child_object_manager_cls ( instance ) <EOL> def __set__ ( self , instance , value ) : <EOL> manager = self . __get__ ( instance ) <EOL> manager . clear ( ) <EOL> manager . add ( * value ) <EOL> @ cached_property <EOL> def child_object_manager_cls ( self ) : <EOL> try : <EOL> rel = self . rel <EOL> except AttributeError : <EOL> rel = self . related <EOL> return create_deferring_foreign_related_manager ( rel , self . related_manager_cls ) <EOL> class ParentalKey ( ForeignKey ) : <EOL> related_accessor_class = ChildObjectsDescriptor <EOL> allow_unsaved_instance_assignment = True <EOL> def contribute_to_related_class ( self , cls , related ) : <EOL> super ( ParentalKey , self ) . contribute_to_related_class ( cls , related ) <EOL> try : <EOL> cls . _meta . child_relations . append ( related ) <EOL> except AttributeError : <EOL> cls . _meta . child_relations = [ related ] <EOL> def check ( self , ** kwargs ) : <EOL> errors = super ( ParentalKey , self ) . check ( ** kwargs ) <EOL> if isinstance ( self . rel . to , type ) and not issubclass ( self . rel . to , ClusterableModel ) : <EOL> errors . append ( <EOL> checks . Error ( <EOL> '<STR_LIT>' , <EOL> hint = '<STR_LIT>' . format ( <EOL> model_name = self . rel . to . _meta . app_label + '<STR_LIT:.>' + self . rel . to . __name__ , <EOL> ) , <EOL> obj = self , <EOL> id = '<STR_LIT>' , <EOL> ) <EOL> ) <EOL> if self . rel . get_accessor_name ( ) == '<STR_LIT:+>' : <EOL> errors . append ( <EOL> checks . Error ( <EOL> "<STR_LIT>" , <EOL> hint = "<STR_LIT>" , <EOL> obj = self , <EOL> id = '<STR_LIT>' , <EOL> ) <EOL> ) <EOL> return errors </s>
