Repository: litestar-org/litestar

Issue:
Bug: SQLAlchemy Plugin DTO cannot reflect relationships.
Hi folks,

not sure if this really a bug or a feature, but Im not able to make a DTO from a instance having a simple relationship.
I do have very often the case where I just want to reflect the 1 in my sa model and explicitly not the n because it will soon pollute the namespace. 
And it is not that uncommon as per [sa doc](https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html#many-to-one)

I could work around by expunging the orm model form the its session and set owner to None. But this implies a lot of additional problems. 

P. S. I really would encourage you to document only working examples. Code below is a copy from sa plugin doc, which does not work without adding a proper tablename. 

cheers...

```python
"""
Showing exception on simple relationships avoiding optional back_populate argument.
"""
from sqlalchemy import Column, Float, ForeignKey, Integer, String
from sqlalchemy.orm import relationship, declarative_base
from starlite.plugins.sql_alchemy import SQLAlchemyPlugin
from starlite import DTOFactory

Base = declarative_base()

dto_factory = DTOFactory(plugins=[SQLAlchemyPlugin()])

class Pet(Base):
    __tablename__ = "pet"  # needs to added to example, or change base definition accordingly.
    id = Column(Integer, primary_key=True)
    name = Column(String)
    age = Column(Float)
    owner_id = Column(Integer, ForeignKey("user.id"))
    owner = relationship("User") #  , back_populates="pets")


class User(Base):
    __tablename__ = "user" # needs to added to example, or change base definition accordingly.
    id = Column(Integer, primary_key=True)
    name = Column(String, default="moishe")
    # pets = relationship(
    #     "Pet",
    #     back_populates="owner",
    # )


UserDTO = dto_factory("UserDTO", User)
PetDTO = dto_factory("PetDTO", Pet)

mike = User(id=1, name="Mike")
kitty = Pet(id=1, name="kitty", age=4.2)
kitty.owner = mike

pet_dto = PetDTO.from_model_instance(kitty)

# prints:
# pydantic.error_wrappers.ValidationError: 1 validation error for Pet
# owner
#   value is not a valid dict (type=type_error.dict)
```



Discussion:
Hi @cloasdata - I would like to ask either @peterschutt or @cofin to look into this when they have time.
Yep I'll be able to have a look

Resolving patch:
diff --git a/starlite/plugins/sql_alchemy/plugin.py b/starlite/plugins/sql_alchemy/plugin.py
--- a/starlite/plugins/sql_alchemy/plugin.py
+++ b/starlite/plugins/sql_alchemy/plugin.py
@@ -390,7 +390,9 @@ def to_pydantic_model_class(self, model_class: Type[DeclarativeMeta], **kwargs:
                     # we are treating back-references as any to avoid infinite recursion
                     else:
                         field_definitions[name] = (Any, None)
-            self._model_namespace_map[model_name] = create_model(model_name, **field_definitions)
+            self._model_namespace_map[model_name] = create_model(
+                model_name, __config__=type("Config", (), {"orm_mode": True}), **field_definitions
+            )
             for related_entity_class in related_entity_classes:
                 self.to_pydantic_model_class(model_class=related_entity_class)
         model = self._model_namespace_map[model_name]
@@ -426,7 +428,7 @@ def to_dict(self, model_instance: "DeclarativeMeta") -> Dict[str, Any]:
         pydantic_model = self._model_namespace_map.get(model_class.__qualname__) or self.to_pydantic_model_class(
             model_class=model_class
         )
-        return pydantic_model(**{field: getattr(model_instance, field) for field in pydantic_model.__fields__}).dict()
+        return pydantic_model.from_orm(model_instance).dict()  # type:ignore[pydantic-unexpected]
 
     def from_dict(self, model_class: "Type[DeclarativeMeta]", **kwargs: Any) -> DeclarativeMeta:
         """Given a dictionary of kwargs, return an instance of the given


Test patch:
diff --git a/tests/plugins/sql_alchemy_plugin/test_sql_alchemy_relationships.py b/tests/plugins/sql_alchemy_plugin/test_sql_alchemy_relationships.py
--- a/tests/plugins/sql_alchemy_plugin/test_sql_alchemy_relationships.py
+++ b/tests/plugins/sql_alchemy_plugin/test_sql_alchemy_relationships.py
@@ -4,7 +4,7 @@
 from typing_extensions import get_args
 
 from starlite.plugins.sql_alchemy import SQLAlchemyPlugin
-from tests.plugins.sql_alchemy_plugin import Pet, User
+from tests.plugins.sql_alchemy_plugin import Company, Pet, User
 
 
 def test_relationship() -> None:
@@ -23,7 +23,11 @@ def test_relationship() -> None:
 
 
 def test_table_name() -> None:
-    pet_table = Pet
-    user_table = User
-    assert pet_table.__tablename__ == "pet"
-    assert user_table.__tablename__ == "user"
+    assert Pet.__tablename__ == "pet"
+    assert User.__tablename__ == "user"
+
+
+def test_plugin_to_dict_with_relationship() -> None:
+    plugin = SQLAlchemyPlugin()
+    user = User(id=1, name="A. Person", company=Company(id=1, name="Mega Corp", worth=1.0))
+    plugin.to_dict(user)  # type:ignore[arg-type]