[docs]classReverseRelation(Generic[MODEL]):""" Relation container for :func:`.ForeignKeyField`. """def__init__(self,remote_model:Type[MODEL],relation_field:str,instance:"Model",from_field:str,)->None:self.remote_model=remote_modelself.relation_field=relation_fieldself.instance=instanceself.from_field=from_fieldself._fetched=Falseself._custom_query=Falseself.related_objects:List[MODEL]=[]@propertydef_query(self)->"QuerySet[MODEL]":ifnotself.instance._saved_in_db:raiseOperationalError("This objects hasn't been instanced, call .save() before calling related queries")returnself.remote_model.filter(**{self.relation_field:getattr(self.instance,self.from_field)})def__contains__(self,item:Any)->bool:self._raise_if_not_fetched()returniteminself.related_objectsdef__iter__(self)->"Iterator[MODEL]":self._raise_if_not_fetched()returnself.related_objects.__iter__()def__len__(self)->int:self._raise_if_not_fetched()returnlen(self.related_objects)def__bool__(self)->bool:self._raise_if_not_fetched()returnbool(self.related_objects)def__getitem__(self,item:int)->MODEL:self._raise_if_not_fetched()returnself.related_objects[item]def__await__(self)->Generator[Any,None,List[MODEL]]:returnself._query.__await__()asyncdef__aiter__(self)->AsyncGenerator[Any,MODEL]:ifnotself._fetched:self._set_result_for_query(awaitself)forvalinself.related_objects:yieldval
[docs]deffilter(self,*args:"Q",**kwargs:Any)->"QuerySet[MODEL]":""" Returns a QuerySet with related elements filtered by args/kwargs. """returnself._query.filter(*args,**kwargs)
[docs]defall(self)->"QuerySet[MODEL]":""" Returns a QuerySet with all related elements. """returnself._query
[docs]deforder_by(self,*orderings:str)->"QuerySet[MODEL]":""" Returns a QuerySet related elements in order. """returnself._query.order_by(*orderings)
[docs]deflimit(self,limit:int)->"QuerySet[MODEL]":""" Returns a QuerySet with at most «limit» related elements. """returnself._query.limit(limit)
[docs]defoffset(self,offset:int)->"QuerySet[MODEL]":""" Returns a QuerySet with all related elements offset by «offset». """returnself._query.offset(offset)
def_set_result_for_query(self,sequence:List[MODEL],attr:Optional[str]=None)->None:self._fetched=Trueself.related_objects=sequenceifattr:setattr(self.instance,attr,sequence)def_raise_if_not_fetched(self)->None:ifnotself._fetched:raiseNoValuesFetched("No values were fetched for this relation, first use .fetch_related()")
[docs]classManyToManyRelation(ReverseRelation[MODEL]):""" Many-to-many relation container for :func:`.ManyToManyField`. """def__init__(self,instance:"Model",m2m_field:"ManyToManyFieldInstance[MODEL]")->None:super().__init__(m2m_field.related_model,m2m_field.related_name,instance,"pk")self.field=m2m_fieldself.instance=instance
[docs]asyncdefadd(self,*instances:MODEL,using_db:"Optional[BaseDBAsyncClient]"=None)->None:""" Adds one or more of ``instances`` to the relation. If it is already added, it will be silently ignored. :raises OperationalError: If Object to add is not saved. """ifnotinstances:returnifnotself.instance._saved_in_db:raiseOperationalError(f"You should first call .save() on {self.instance}")db=using_dborself.remote_model._meta.dbpk_formatting_func=type(self.instance)._meta.pk.to_db_valuerelated_pk_formatting_func=type(instances[0])._meta.pk.to_db_valuepk_b=pk_formatting_func(self.instance.pk,self.instance)pks_f:list=[]forinstance_to_addininstances:ifnotinstance_to_add._saved_in_db:raiseOperationalError(f"You should first call .save() on {instance_to_add}")pk_f=related_pk_formatting_func(instance_to_add.pk,instance_to_add)pks_f.append(pk_f)through_table=Table(self.field.through)backward_key,forward_key=self.field.backward_key,self.field.forward_keybackward_field,forward_field=through_table[backward_key],through_table[forward_key]select_query=(db.query_class.from_(through_table).where(backward_field==pk_b).select(forward_key))criterion=forward_field==pks_f[0]iflen(pks_f)==1elseforward_field.isin(pks_f)select_query=select_query.where(criterion)_,already_existing_relations_raw=awaitdb.execute_query(str(select_query))already_existing_forward_pks={related_pk_formatting_func(r[forward_key],self.instance)forrinalready_existing_relations_raw}ifpks_f_to_insert:=set(pks_f)-already_existing_forward_pks:query=db.query_class.into(through_table).columns(forward_field,backward_field)forpk_finpks_f_to_insert:query=query.insert(pk_f,pk_b)awaitdb.execute_query(str(query))
[docs]asyncdefclear(self,using_db:"Optional[BaseDBAsyncClient]"=None)->None:""" Clears ALL relations. """awaitself._remove_or_clear(using_db=using_db)
[docs]asyncdefremove(self,*instances:MODEL,using_db:"Optional[BaseDBAsyncClient]"=None)->None:""" Removes one or more of ``instances`` from the relation. :raises OperationalError: remove() was called with no instances. """ifnotinstances:raiseOperationalError("remove() called on no instances")awaitself._remove_or_clear(instances,using_db)
classRelationalField(Field[MODEL]):has_db_field=Falsedef__init__(self,related_model:"Type[MODEL]",to_field:Optional[str]=None,db_constraint:bool=True,**kwargs:Any,)->None:super().__init__(**kwargs)self.related_model:"Type[MODEL]"=related_modelself.to_field:str=to_field# type: ignoreself.to_field_instance:Field=None# type: ignoreself.db_constraint=db_constraintifTYPE_CHECKING:@overloaddef__get__(self,instance:None,owner:Type["Model"])->"RelationalField[MODEL]":...@overloaddef__get__(self,instance:"Model",owner:Type["Model"])->MODEL:...def__get__(self,instance:Optional["Model"],owner:Type["Model"])->"RelationalField[MODEL] | MODEL":...def__set__(self,instance:"Model",value:MODEL)->None:...defdescribe(self,serializable:bool)->dict:desc=super().describe(serializable)desc["db_constraint"]=self.db_constraintdeldesc["db_column"]returndesc@classmethoddefvalidate_model_name(cls,model_name:str)->None:iflen(model_name.split("."))!=2:field_type=cls.__name__.replace("Instance","")raiseConfigurationError(f'{field_type} accepts model name in format "app.Model"')classForeignKeyFieldInstance(RelationalField[MODEL]):def__init__(self,model_name:str,related_name:Union[Optional[str],Literal[False]]=None,on_delete:OnDelete=CASCADE,**kwargs:Any,)->None:super().__init__(None,**kwargs)# type: ignoreself.validate_model_name(model_name)self.model_name=model_nameself.related_name=related_nameifon_deletenotinset(OnDelete):raiseConfigurationError("on_delete can only be CASCADE, RESTRICT, SET_NULL, SET_DEFAULT or NO_ACTION")ifon_delete==SET_NULLandnotbool(kwargs.get("null")):raiseConfigurationError("If on_delete is SET_NULL, then field must have null=True set")self.on_delete=on_deletedefdescribe(self,serializable:bool)->dict:desc=super().describe(serializable)desc["raw_field"]=self.source_fielddesc["on_delete"]=str(self.on_delete)returndescclassBackwardFKRelation(RelationalField[MODEL]):def__init__(self,field_type:"Type[MODEL]",relation_field:str,relation_source_field:str,null:bool,description:Optional[str],**kwargs:Any,)->None:super().__init__(field_type,null=null,**kwargs)self.relation_field:str=relation_fieldself.relation_source_field:str=relation_source_fieldself.description:Optional[str]=descriptionclassOneToOneFieldInstance(ForeignKeyFieldInstance[MODEL]):def__init__(self,model_name:str,related_name:Union[Optional[str],Literal[False]]=None,on_delete:OnDelete=CASCADE,**kwargs:Any,)->None:self.validate_model_name(model_name)super().__init__(model_name,related_name,on_delete,unique=True,**kwargs)classBackwardOneToOneRelation(BackwardFKRelation[MODEL]):passclassManyToManyFieldInstance(RelationalField[MODEL]):field_type=ManyToManyRelationdef__init__(self,model_name:str,through:Optional[str]=None,forward_key:Optional[str]=None,backward_key:str="",related_name:str="",on_delete:OnDelete=CASCADE,field_type:"Type[MODEL]"=None,# type: ignorecreate_unique_index:bool=True,**kwargs:Any,)->None:# TODO: rename through to through_table# TODO: add through to use a Modelsuper().__init__(field_type,**kwargs)self.validate_model_name(model_name)self.model_name:str=model_nameself.related_name:str=related_nameself.forward_key:str=forward_keyorf"{model_name.split('.')[1].lower()}_id"self.backward_key:str=backward_keyself.through:str=through# type: ignoreself._generated:bool=Falseself.on_delete=on_deleteself.create_unique_index=create_unique_indexdefdescribe(self,serializable:bool)->dict:desc=super().describe(serializable)desc["model_name"]=self.model_namedesc["related_name"]=self.related_namedesc["forward_key"]=self.forward_keydesc["backward_key"]=self.backward_keydesc["through"]=self.throughdesc["on_delete"]=str(self.on_delete)desc["_generated"]=self._generatedreturndesc@overloaddefOneToOneField(model_name:str,related_name:Union[Optional[str],Literal[False]]=None,on_delete:OnDelete=CASCADE,db_constraint:bool=True,*,null:Literal[True],**kwargs:Any,)->"OneToOneNullableRelation[MODEL]":...@overloaddefOneToOneField(model_name:str,related_name:Union[Optional[str],Literal[False]]=None,on_delete:OnDelete=CASCADE,db_constraint:bool=True,null:Literal[False]=False,**kwargs:Any,)->"OneToOneRelation[MODEL]":...
[docs]defOneToOneField(model_name:str,related_name:Union[Optional[str],Literal[False]]=None,on_delete:OnDelete=CASCADE,db_constraint:bool=True,null:bool=False,**kwargs:Any,)->"OneToOneRelation[MODEL] | OneToOneNullableRelation[MODEL]":""" OneToOne relation field. This field represents a foreign key relation to another model. See :ref:`one_to_one` for usage information. You must provide the following: ``model_name``: The name of the related model in a :samp:`'{app}.{model}'` format. The following is optional: ``related_name``: The attribute name on the related model to reverse resolve the foreign key. ``on_delete``: One of: ``field.CASCADE``: Indicate that the model should be cascade deleted if related model gets deleted. ``field.RESTRICT``: Indicate that the related model delete will be restricted as long as a foreign key points to it. ``field.SET_NULL``: Resets the field to NULL in case the related model gets deleted. Can only be set if field has ``null=True`` set. ``field.SET_DEFAULT``: Resets the field to ``default`` value in case the related model gets deleted. Can only be set is field has a ``default`` set. ``field.NO_ACTION``: Take no action. ``to_field``: The attribute name on the related model to establish foreign key relationship. If not set, pk is used ``db_constraint``: Controls whether or not a constraint should be created in the database for this foreign key. The default is True, and that’s almost certainly what you want; setting this to False can be very bad for data integrity. """returnOneToOneFieldInstance(model_name,related_name,on_delete,db_constraint=db_constraint,null=null,**kwargs)
[docs]defForeignKeyField(model_name:str,related_name:Union[Optional[str],Literal[False]]=None,on_delete:OnDelete=CASCADE,db_constraint:bool=True,null:bool=False,**kwargs:Any,)->"ForeignKeyRelation[MODEL] | ForeignKeyNullableRelation[MODEL]":""" ForeignKey relation field. This field represents a foreign key relation to another model. See :ref:`foreign_key` for usage information. You must provide the following: ``model_name``: The name of the related model in a :samp:`'{app}.{model}'` format. The following is optional: ``related_name``: The attribute name on the related model to reverse resolve the foreign key. ``on_delete``: One of: ``field.CASCADE``: Indicate that the model should be cascade deleted if related model gets deleted. ``field.RESTRICT``: Indicate that the related model delete will be restricted as long as a foreign key points to it. ``field.SET_NULL``: Resets the field to NULL in case the related model gets deleted. Can only be set if field has ``null=True`` set. ``field.SET_DEFAULT``: Resets the field to ``default`` value in case the related model gets deleted. Can only be set is field has a ``default`` set. ``field.NO_ACTION``: Take no action. ``to_field``: The attribute name on the related model to establish foreign key relationship. If not set, pk is used ``db_constraint``: Controls whether or not a constraint should be created in the database for this foreign key. The default is True, and that’s almost certainly what you want; setting this to False can be very bad for data integrity. """returnForeignKeyFieldInstance(model_name,related_name,on_delete,db_constraint=db_constraint,null=null,**kwargs)
[docs]defManyToManyField(model_name:str,through:Optional[str]=None,forward_key:Optional[str]=None,backward_key:str="",related_name:str="",on_delete:OnDelete=CASCADE,db_constraint:bool=True,create_unique_index:bool=True,**kwargs:Any,)->"ManyToManyRelation[Any]":""" ManyToMany relation field. This field represents a many-to-many between this model and another model. See :ref:`many_to_many` for usage information. You must provide the following: ``model_name``: The name of the related model in a :samp:`'{app}.{model}'` format. The following is optional: ``through``: The DB table that represents the through table. The default is normally safe. ``forward_key``: The forward lookup key on the through table. The default is normally safe. ``backward_key``: The backward lookup key on the through table. The default is normally safe. ``related_name``: The attribute name on the related model to reverse resolve the many to many. ``db_constraint``: Controls whether or not a constraint should be created in the database for this foreign key. The default is True, and that’s almost certainly what you want; setting this to False can be very bad for data integrity. ``on_delete``: One of: ``field.CASCADE``: Indicate that the model should be cascade deleted if related model gets deleted. ``field.RESTRICT``: Indicate that the related model delete will be restricted as long as a foreign key points to it. ``field.SET_NULL``: Resets the field to NULL in case the related model gets deleted. Can only be set if field has ``null=True`` set. ``field.SET_DEFAULT``: Resets the field to ``default`` value in case the related model gets deleted. Can only be set is field has a ``default`` set. ``field.NO_ACTION``: Take no action. ``create_unique_index``: Controls whether or not a unique index should be created in the database to speed up select queries. The default is True. If you want to allow repeat records, set this to False. """returnManyToManyFieldInstance(# type: ignoremodel_name,through,forward_key,backward_key,related_name,on_delete=on_delete,db_constraint=db_constraint,create_unique_index=create_unique_index,**kwargs,)
OneToOneNullableRelation=Optional[OneToOneFieldInstance[MODEL]]"""Type hint for the result of accessing the :func:`.OneToOneField` field in the modelwhen obtained model can be nullable."""OneToOneRelation=OneToOneFieldInstance[MODEL]"""Type hint for the result of accessing the :func:`.OneToOneField` field in the model."""ForeignKeyNullableRelation=Optional[ForeignKeyFieldInstance[MODEL]]"""Type hint for the result of accessing the :func:`.ForeignKeyField` field in the modelwhen obtained model can be nullable."""ForeignKeyRelation=ForeignKeyFieldInstance[MODEL]"""Type hint for the result of accessing the :func:`.ForeignKeyField` field in the model."""