# How to Configure Automatic Join Condition Detection in FastCRUD

> Learn how to configure automatic join condition detection in FastCRUD with auto_detect_relationships. Simplify your SQLAlchemy configurations and streamline your development.

- Repository: [Benav Labs/fastcrud](https://github.com/benavlabs/fastcrud)
- Tags: how-to-guide
- Published: 2026-02-26

---

**FastCRUD automatically infers SQLAlchemy join conditions through the `auto_detect_relationships` parameter, eliminating manual `join_on` configuration while supporting selective relationship inclusion and safe one-to-many handling.**

When working with complex SQLAlchemy models in the `benavlabs/fastcrud` repository, manually specifying join conditions for every query becomes repetitive and error-prone. FastCRUD solves this by providing automatic join condition detection that inspects your model's relationships and generates the appropriate `JoinConfig` objects dynamically. This guide explains how to configure and use this feature effectively across the core CRUD methods.

## Understanding the `auto_detect_relationships` Parameter

The `auto_detect_relationships` parameter is a versatile configuration option accepted by `get_joined`, `get_multi_joined`, `get`, and `count` methods in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py). It accepts three possible values:

- **`False`** (default): Disables automatic detection; you must provide manual join configuration
- **`True`**: Enables detection of all relationships except one-to-many associations
- **`Sequence[str]`** (e.g., `["tier", "department"]`): Enables detection only for the specified relationship names

When enabled, FastCRUD inspects the SQLAlchemy mapper of your primary model via `inspect(self.model).relationships` to discover foreign key relationships bidirectionally.

## How Automatic Join Detection Works

### Step 1: Parameter Parsing

FastCRUD checks the `auto_detect_relationships` argument at the beginning of method execution. If the value is truthy (boolean `True` or a non-empty list), the system enters the auto-detect branch and validates that no manual join parameters (`join_model`, `joins_config`, etc.) are present. This validation occurs at line 1830 in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py).

### Step 2: Relationship Inspection

The system calls `inspect(self.model).relationships` to access SQLAlchemy's relationship metadata. For each relationship, FastCRUD extracts foreign-key columns from both sides of the relationship, enabling bidirectional detection regardless of which model defined the `ForeignKey` constraint.

### Step 3: JoinConfig Creation

For every discovered relationship, FastCRUD automatically constructs a `JoinConfig` object containing:
- The target model class
- The inferred join condition
- A default `join_prefix` based on the relationship name
- The `relationship_type` (one-to-one, one-to-many, many-to-one)

If you provided a specific list of relationship names, the system filters the discovered relationships accordingly at line 1835.

### Step 4: One-to-Many Safety

By default, FastCRUD excludes one-to-many relationships from automatic detection because a plain SQL join would duplicate the parent row for each child record, potentially exploding the result set. To include one-to-many relationships, you must either:
- Set `include_one_to_many=True` globally
- Explicitly list the one-to-many relationship name in the sequence argument

When a specific relationship name is provided, FastCRUD automatically treats it as a one-to-many join even without the global flag.

### Step 5: Execution

The generated `JoinConfig` objects are passed to the internal join engine (`_apply_joins`) exactly as if you had supplied them manually. All subsequent features—nesting (`nest_joins`), filtering, sorting, and pagination—function identically regardless of whether the configuration was auto-detected or manually specified.

## Configuring Automatic Join Detection in Practice

### Auto-Detect All Relationships

To automatically join all one-to-one and many-to-one relationships defined on your model:

```python
user = await user_crud.get_joined(
    db=db,
    schema_to_select=ReadUserSchema,
    auto_detect_relationships=True,
    nest_joins=True,
    id=1,
)

```

This returns a nested structure like:

```json
{
  "id": 1,
  "name": "Alice",
  "tier": { "id": 2, "name": "Premium" },
  "department": { "id": 5, "name": "Engineering" }
}

```

### Select Specific Relationships

To limit automatic detection to specific relationships, pass a list of relationship names as defined in your SQLAlchemy model:

```python
user = await user_crud.get_joined(
    db=db,
    schema_to_select=ReadUserSchema,
    auto_detect_relationships=["tier"],  # Only join the tier relationship

    nest_joins=True,
    id=1,
)

```

### Include One-to-Many Relationships

To safely include one-to-many relationships, either enable them globally or specify them explicitly:

```python

# Method 1: Global flag

user = await user_crud.get_joined(
    db=db,
    schema_to_select=ReadUserSchema,
    auto_detect_relationships=True,
    include_one_to_many=True,  # Enable one-to-many joins

    nest_joins=True,
    id=1,
)

# Method 2: Explicit listing (bypasses the safety check)

user = await user_crud.get_joined(
    db=db,
    schema_to_select=ReadUserSchema,
    auto_detect_relationships=["posts"],  # Explicitly include the one-to-many relationship

    nest_joins=True,
    id=1,
)

```

### Error Handling When Mixing Manual and Auto-Detect

FastCRUD strictly prevents mixing automatic detection with manual join configuration to avoid ambiguous join plans. Attempting to combine them raises a `ValueError`:

```python

# This will raise: ValueError: Cannot use auto_detect_relationships with manual join parameters.

await user_crud.get_joined(
    db=db,
    schema_to_select=ReadUserSchema,
    auto_detect_relationships=True,
    join_model=Tier,  # Manual join parameter - incompatible

    id=1,
)

```

## Important Configuration Rules

When configuring automatic join condition detection in FastCRUD, adhere to these critical constraints:

- **Exclusive Use**: You cannot combine `auto_detect_relationships` with manual join arguments such as `join_model` or `joins_config`. FastCRUD raises a `ValueError` at line 1830 in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py) if you attempt to mix these approaches.

- **Selectivity**: Pass a list of relationship names (e.g., `["tier", "department"]`) to limit automatic detection to specific associations. This filters the discovered relationships at line 1835 in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py).

- **Graceful Fallback**: If your SQLAlchemy model defines no relationships, FastCRUD automatically falls back to standard `get` or `get_multi` logic, returning only the primary record without raising an error.

## Summary

- FastCRUD provides **automatic join condition detection** via the `auto_detect_relationships` parameter available in `get_joined`, `get_multi_joined`, `get`, and `count` methods.
- The system inspects SQLAlchemy relationship metadata in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py) to generate `JoinConfig` objects dynamically, supporting bidirectional foreign key detection.
- **One-to-many relationships are excluded by default** to prevent row duplication; enable them with `include_one_to_many=True` or by explicitly listing the relationship name.
- **Manual join parameters are incompatible** with auto-detection; attempting to mix them raises a `ValueError`.
- Use a **list of relationship names** to selectively auto-detect specific associations while ignoring others.

## Frequently Asked Questions

### What methods support auto_detect_relationships in FastCRUD?

The `auto_detect_relationships` parameter is supported by four core CRUD methods in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py): `get_joined`, `get_multi_joined`, `get`, and `count`. This allows you to leverage automatic join detection whether you are fetching a single record, multiple records, or simply counting results with joined relationships.

### Why are one-to-many relationships excluded by default when using auto_detect_relationships?

One-to-many relationships are excluded by default because a standard SQL join duplicates the parent row for each child record, potentially causing an exponential explosion in result set size. FastCRUD prioritizes safe defaults to prevent performance issues and unexpected data duplication. You can explicitly enable one-to-many joins by setting `include_one_to_many=True` or by specifically naming the relationship in the `auto_detect_relationships` list.

### Can I mix auto_detect_relationships with manual join parameters like join_model?

No, FastCRUD strictly prohibits mixing automatic join detection with manual join configuration. If you attempt to use `auto_detect_relationships` alongside parameters like `join_model`, `joins_config`, or other manual join arguments, FastCRUD raises a `ValueError` with the message "Cannot use auto_detect_relationships with manual join parameters." This enforcement occurs early in the method execution at line 1830 in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py) to prevent ambiguous join plans.

### How does FastCRUD handle models that have no relationships defined?

If your SQLAlchemy model has no relationships defined, FastCRUD gracefully falls back to standard retrieval logic. The system will simply return the primary record without attempting any joins, effectively behaving like the standard `get` or `get_multi` methods. This ensures that enabling `auto_detect_relationships` on models with varying relationship definitions does not cause runtime errors.