Repository: litestar-org/litestar

Issue:
Bug: SQA DTO touches ignored properties triggering lazy loading
### Description

The SQA DTO engine doesn't make use of ignored properties to skip touching SQA object properties, in particular lazy loaded properties that get loaded when the DTO touches them.

As a minimal (but not complete) example.

```python
class Fund(Base):
  name: Mapped[str]
  charges: Mapped[list[Charge]] = relationship(lazy=True, ...)

FundDTO = SQLAlchemyDTO[
  Annotated[
    Fund,
    DTOConfig(include={"name"}),
  ],
]

@get("/fund", dto=FundDTO)
def fetch_one_fund(fund_number: str, *, db_session: Session) -> Fund:
  if fund := db_session.get(Fund, fund_number):
    return fund
  raise NotFoundException
```

Despite explicitly including only the `"name"` property and implicitly ignoring the `"charges"` property, `charges` still gets loaded in. The DTO returned object is just `{"name": ...}`. (I only noticed this because the `charges` table is quite large and the route gets quote slow.)

This currently be alleviated by explicitly specifying `noload` for ignored properties.

```python
if fund := db_session.get(Fund, fund_number, options=[noload(Fund.charges)]):
  ...
```

But doesn't seem ideal, and instead the DTO should only be touching non-ignored properties.

### URL to code causing the issue

_No response_

### MCVE

_No response_

### Steps to reproduce

_No response_

### Screenshots

_No response_

### Logs

_No response_

### Litestar Version

2.0.0b4

### Platform

- [X] Linux
- [X] Mac
- [ ] Windows
- [ ] Other (Please specify in the description above)

<!-- POLAR PLEDGE BADGE START -->
---

## Funding
* If you would like to see an issue prioritized, make a pledge towards it!
* We receive the pledge once the issue is completed & verified

<a href="https://polar.sh/litestar-org/litestar/issues/2125">
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/api/github/litestar-org/litestar/issues/2125/pledge.svg?darkmode=1">
  <img alt="Fund with Polar" src="https://polar.sh/api/github/litestar-org/litestar/issues/2125/pledge.svg">
</picture>
</a>
<!-- POLAR PLEDGE BADGE END -->

StaticFilesConfig and virtual directories
I'm trying to write a ``FileSystemProtocol`` to load files from the package data using [importlib_resources](https://importlib-resources.readthedocs.io/en/latest/using.html#). But because ``directories`` is defined as ``DirectoryPath``, pydantic checks if the given directories exist in the local filesystem. 

This is not generally true, especially in any kind of virtual filesystem (e.g. a zipped package). I think this condition should be relaxed to support virtual filesystems.

https://github.com/starlite-api/starlite/blob/9bb6dcd57c10a591377cf8e3a537e9292566d5b9/starlite/config/static_files.py#L32


Discussion:

I agree. Do you want to submit a PR?

Resolving patch:
diff --git a/litestar/dto/_backend.py b/litestar/dto/_backend.py
--- a/litestar/dto/_backend.py
+++ b/litestar/dto/_backend.py
@@ -583,13 +583,14 @@ def _transfer_instance_data(
         should_use_serialization_name = is_data_field and not is_dto_data_type
         source_name = field_definition.serialization_name if should_use_serialization_name else field_definition.name
 
-        source_has_value = (
+        if not is_data_field:
+            if field_definition.is_excluded:
+                continue
+        elif not (
             source_name in source_instance
             if isinstance(source_instance, Mapping)
             else hasattr(source_instance, source_name)
-        )
-
-        if (is_data_field and not source_has_value) or (not is_data_field and field_definition.is_excluded):
+        ):
             continue
 
         transfer_type = field_definition.transfer_type


Test patch:
diff --git a/tests/unit/test_dto/test_factory/test_backends/test_backends.py b/tests/unit/test_dto/test_factory/test_backends/test_backends.py
--- a/tests/unit/test_dto/test_factory/test_backends/test_backends.py
+++ b/tests/unit/test_dto/test_factory/test_backends/test_backends.py
@@ -4,6 +4,7 @@
 from dataclasses import dataclass, field
 from types import ModuleType
 from typing import TYPE_CHECKING, Callable, List, Optional
+from unittest.mock import MagicMock
 
 import pytest
 from msgspec import Struct, to_builtins
@@ -294,6 +295,37 @@ def test_backend_encode_collection_data(
     assert encode_json(data) == COLLECTION_RAW
 
 
+def test_transfer_only_touches_included_attributes() -> None:
+    """Ensure attribute that are not included are never touched in any way during
+    transfer.
+
+    https://github.com/litestar-org/litestar/issues/2125
+    """
+    mock = MagicMock()
+
+    @dataclass()
+    class Foo:
+        id: str
+        bar: str = ""
+
+    class Factory(DataclassDTO):
+        config = DTOConfig(include={"excluded"})
+
+    backend = DTOBackend(
+        handler_id="test",
+        dto_factory=Factory,
+        field_definition=TransferDTOFieldDefinition.from_annotation(Foo),
+        model_type=Foo,
+        wrapper_attribute_name=None,
+        is_data_field=False,
+    )
+
+    Foo.bar = property(fget=lambda s: mock(return_value=""), fset=lambda s, v: None)  # type: ignore[assignment]
+
+    backend.encode_data(Foo(id="1"))
+    assert mock.call_count == 0
+
+
 def test_parse_model_nested_exclude(create_module: Callable[[str], ModuleType]) -> None:
     module = create_module(
         """