From 9952e81b59010d75b206fd8afe138785d8c9039b Mon Sep 17 00:00:00 2001 From: romanov Date: Wed, 20 Jul 2022 12:02:44 +0300 Subject: [PATCH 1/3] join_m2m with docstring --- piccolo/table.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/piccolo/table.py b/piccolo/table.py index a792db105..b49faad2d 100644 --- a/piccolo/table.py +++ b/piccolo/table.py @@ -606,6 +606,104 @@ def remove_m2m(self, *rows: Table, m2m: M2M) -> M2MRemoveRelated: m2m=m2m, ) + async def __join_field(self, field: str, ignore: bool=False)->list: + """ + Runs get_m2m for a FIELD of object. Catches ValueError, when there are + no relations in M2M table and returns empty list(). If ignore flag is + True, also returns empty list + Args: + field (str): Attr name + ignore (bool, optional): Flag for include and exclude logic of join_m2m(). Defaults to False. + Returns: + list: list of related objects, or an empty one + """ + if ignore: + return list() + try: + return await self.get_m2m(self.__getattribute__(field)).run() + except ValueError: + return list() + + async def join_m2m( + self, + include_fields: set[str] | list[str] | None=None, + exclude_fields: set[str] | list[str] | None=None + ): + """ + Runs get_m2m() method for all M2M fields of object. Can be useful for + complex PyDantic models in READ actions. Returns empty list() for an + attribute, if there are no relations to this object. + + Optional, you can include or exclude fields to define which attrs should + be joined. Setting either include_fields, and exclude_fields will raise + AssertionError. + + Model example: + .. code-block:: python + class Band(Table): + #some fields... + genres = m2m.M2M(LazyTableReference("BandtoGenre", module_path=__name__)) + concerts = m2m.M2M(LazyTableReference("BandToConcert", module_path=__name__)) + .. code-block:: python + >>> band = await Band.objects().get(Band.name == "Pythonistas") + >>> await band.join_m2m() + >>> band.genres + [, ] + >>> band.concerts + [,,] + include_fields example: + .. code-block:: python + >>> await band.join_m2m(include_fields=['genres']) + >>> band.genres + [, ] + >>> band.concerts + [] + exclude_fields example: + .. code-block:: python + >>> await band.join_m2m(exclude_fields=['genres']) + >>> band.genres + [] + >>> band.concerts + [,,] + + Args: + include_fields (set[str] | list[str] | None, optional): Only this fields will be joined to base model`s object. + Defaults to None. + exclude_fields (set[str] | list[str] | None, optional): This fields will be excluded from join. + Defaults to None. + """ + assert (include_fields==None) or (exclude_fields==None), "Only one of FIELDS arguments can exist" + if not include_fields is None: + assert isinstance(include_fields,set | list), "include_fields MUST be set, list or None" + if not exclude_fields is None: + assert isinstance(exclude_fields,set | list), "exclude_fields MUST be set, list or None" + m2m_fields: set = set([field for field, object in inspect.getmembers( + self, + lambda a:( + isinstance( + a, + M2M + ) + ) + ) + ]) + ignore_fields: list = list() + if include_fields: + ignore_fields = list(m2m_fields.difference(set(include_fields))) + if exclude_fields: + ignore_fields = list(m2m_fields.intersection(set(exclude_fields))) + for field in list(m2m_fields): + ignore: bool = False + if field in ignore_fields: + ignore = True + self.__setattr__( + field, #M2M attr name + await self.__join_field( + field=field, + ignore=ignore + ) + ) + def to_dict(self, *columns: Column) -> t.Dict[str, t.Any]: """ A convenience method which returns a dictionary, mapping column names From e2a3ce5e611d2d6900712232ba5055b4feb6c6b2 Mon Sep 17 00:00:00 2001 From: northpowered Date: Mon, 6 Mar 2023 13:44:01 +0300 Subject: [PATCH 2/3] Typing adoptation --- piccolo/table.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/piccolo/table.py b/piccolo/table.py index b49faad2d..0d796fc08 100644 --- a/piccolo/table.py +++ b/piccolo/table.py @@ -625,10 +625,10 @@ async def __join_field(self, field: str, ignore: bool=False)->list: return list() async def join_m2m( - self, - include_fields: set[str] | list[str] | None=None, - exclude_fields: set[str] | list[str] | None=None - ): + self, + include_fields: t.Union[set[str], t.List[str], None] = None, + exclude_fields: t.Union[set[str], t.List[str], None] = None + ): """ Runs get_m2m() method for all M2M fields of object. Can be useful for complex PyDantic models in READ actions. Returns empty list() for an @@ -672,13 +672,13 @@ class Band(Table): exclude_fields (set[str] | list[str] | None, optional): This fields will be excluded from join. Defaults to None. """ - assert (include_fields==None) or (exclude_fields==None), "Only one of FIELDS arguments can exist" + assert (include_fields == None) or (exclude_fields == None), "Only one of FIELDS arguments can exist" if not include_fields is None: - assert isinstance(include_fields,set | list), "include_fields MUST be set, list or None" + assert isinstance(include_fields, t.Union[set, list]), "include_fields MUST be set, list or None" if not exclude_fields is None: - assert isinstance(exclude_fields,set | list), "exclude_fields MUST be set, list or None" + assert isinstance(exclude_fields, t.Union[set, list]), "exclude_fields MUST be set, list or None" m2m_fields: set = set([field for field, object in inspect.getmembers( - self, + self, lambda a:( isinstance( a, @@ -697,7 +697,7 @@ class Band(Table): if field in ignore_fields: ignore = True self.__setattr__( - field, #M2M attr name + field, # M2M attr name await self.__join_field( field=field, ignore=ignore From a7e5ebaa03e2d108cf6ded245f01ed710acb9c44 Mon Sep 17 00:00:00 2001 From: Daniel Townsend Date: Mon, 6 Mar 2023 14:53:34 +0000 Subject: [PATCH 3/3] fix linter errors --- piccolo/table.py | 121 +++++++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/piccolo/table.py b/piccolo/table.py index 0d796fc08..fd45e6490 100644 --- a/piccolo/table.py +++ b/piccolo/table.py @@ -606,16 +606,19 @@ def remove_m2m(self, *rows: Table, m2m: M2M) -> M2MRemoveRelated: m2m=m2m, ) - async def __join_field(self, field: str, ignore: bool=False)->list: + async def __join_field(self, field: str, ignore: bool = False) -> list: """ Runs get_m2m for a FIELD of object. Catches ValueError, when there are no relations in M2M table and returns empty list(). If ignore flag is - True, also returns empty list - Args: - field (str): Attr name - ignore (bool, optional): Flag for include and exclude logic of join_m2m(). Defaults to False. - Returns: - list: list of related objects, or an empty one + True, also returns empty list. + + :param field: + The field to join. + :param ignore: + Flag for include and exclude logic of join_m2m(). + :returns: + A list of related objects, or an empty one if none exist. + """ if ignore: return list() @@ -627,81 +630,99 @@ async def __join_field(self, field: str, ignore: bool=False)->list: async def join_m2m( self, include_fields: t.Union[set[str], t.List[str], None] = None, - exclude_fields: t.Union[set[str], t.List[str], None] = None + exclude_fields: t.Union[set[str], t.List[str], None] = None, ): """ Runs get_m2m() method for all M2M fields of object. Can be useful for complex PyDantic models in READ actions. Returns empty list() for an attribute, if there are no relations to this object. - Optional, you can include or exclude fields to define which attrs should - be joined. Setting either include_fields, and exclude_fields will raise - AssertionError. + Optional, you can include or exclude fields to define which attrs + should be joined. Setting either include_fields, and exclude_fields + will raise AssertionError. Model example: + .. code-block:: python + class Band(Table): - #some fields... - genres = m2m.M2M(LazyTableReference("BandtoGenre", module_path=__name__)) - concerts = m2m.M2M(LazyTableReference("BandToConcert", module_path=__name__)) + genres = m2m.M2M(LazyTableReference( + "BandtoGenre", + module_path=__name_ + ) + ) + concerts = m2m.M2M( + LazyTableReference( + "BandToConcert", + module_path=__name__ + ) + ) + .. code-block:: python + >>> band = await Band.objects().get(Band.name == "Pythonistas") >>> await band.join_m2m() >>> band.genres - [, ] + [, ] >>> band.concerts - [,,] - include_fields example: + [,,] + + Include_fields example: + .. code-block:: python >>> await band.join_m2m(include_fields=['genres']) >>> band.genres - [, ] + [, ] >>> band.concerts - [] - exclude_fields example: + [] + + Exclude_fields example: + .. code-block:: python >>> await band.join_m2m(exclude_fields=['genres']) >>> band.genres - [] + [] >>> band.concerts - [,,] - - Args: - include_fields (set[str] | list[str] | None, optional): Only this fields will be joined to base model`s object. - Defaults to None. - exclude_fields (set[str] | list[str] | None, optional): This fields will be excluded from join. - Defaults to None. - """ - assert (include_fields == None) or (exclude_fields == None), "Only one of FIELDS arguments can exist" - if not include_fields is None: - assert isinstance(include_fields, t.Union[set, list]), "include_fields MUST be set, list or None" - if not exclude_fields is None: - assert isinstance(exclude_fields, t.Union[set, list]), "exclude_fields MUST be set, list or None" - m2m_fields: set = set([field for field, object in inspect.getmembers( - self, - lambda a:( - isinstance( - a, - M2M - ) + [,,] + + :param include_fields: + Only these fields will be joined to base model`s object. Defaults + to None. + :param exclude_fields: + Only these fields will be excluded from the join. Defaults to None. + + """ + assert (include_fields is None) or ( + exclude_fields is None + ), "Only one of FIELDS arguments can exist" + if include_fields is not None: + assert isinstance( + include_fields, (set, list) + ), "include_fields MUST be set, list or None" + if exclude_fields is not None: + assert isinstance( + exclude_fields, (set, list) + ), "exclude_fields MUST be set, list or None" + m2m_fields: set = set( + [ + field + for field, object in inspect.getmembers( + self, lambda a: (isinstance(a, M2M)) ) - ) - ]) + ] + ) ignore_fields: list = list() if include_fields: ignore_fields = list(m2m_fields.difference(set(include_fields))) if exclude_fields: ignore_fields = list(m2m_fields.intersection(set(exclude_fields))) - for field in list(m2m_fields): + for field_ in list(m2m_fields): ignore: bool = False - if field in ignore_fields: + if field_ in ignore_fields: ignore = True self.__setattr__( - field, # M2M attr name - await self.__join_field( - field=field, - ignore=ignore - ) + field_, # M2M attr name + await self.__join_field(field=field_, ignore=ignore), ) def to_dict(self, *columns: Column) -> t.Dict[str, t.Any]: