Issue: Join with join_type=JOIN.NATURAL requires explicit join condition
Author: jbender
State: closed
Created: 2018-08-23 22:20:33

Error being thrown:

```
Traceback (most recent call last):
  File "example.py", line 35, in <module>
    .join_from(ModelA, ModelB, JOIN.NATURAL)
  File "/usr/local/lib/python3.7/site-packages/peewee.py", line 6061, in join_from
    return self.join(dest, join_type, on, src, attr)
  File "/usr/local/lib/python3.7/site-packages/peewee.py", line 606, in inner
    method(clone, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/peewee.py", line 6050, in join
    on, attr, constructor = self._normalize_join(src, dest, on, attr)
  File "/usr/local/lib/python3.7/site-packages/peewee.py", line 5978, in _normalize_join
    src_model, dest_model, to_field, on)
  File "/usr/local/lib/python3.7/site-packages/peewee.py", line 6027, in _generate_on_clause
    (src, dest))
ValueError: Unable to find foreign key between <Model: ModelA> and <Model: ModelB>. Please specify an explicit join condition.
```
Natural joins shouldn't need to specify the columns that are being joined, that should be worked out by the database.

Test case that throws an error:

```python
from peewee import JOIN, IntegerField, Model, PostgresqlDatabase

db = PostgresqlDatabase('test_db')

class BaseModel(Model):
  class Meta:
    database = db

class ModelA(BaseModel):
  class Meta:
    table_name = 'model_a'

  model_a_id = IntegerField()

class ModelB(BaseModel):
  class Meta:
    table_name = 'model_b'

  model_b_id = IntegerField()
  model_a_id = IntegerField()

class ModelC(BaseModel):
  class Meta:
    table_name = 'model_c'

  model_c_id = IntegerField()
  model_a_id = IntegerField()


with db:
  db.create_tables([ModelA, ModelB, ModelC])

(ModelA
  .select(ModelA, ModelB, ModelC)
  .join_from(ModelA, ModelB, JOIN.NATURAL)
  .join_from(ModelA, ModelC, JOIN.NATURAL)
).execute()
```

Query that I think *should* be generated (which works):
```sql
SELECT
  model_a.*,
  model_b.*,
  model_c.*
FROM model_a
NATURAL JOIN model_b
NATURAL JOIN model_c;
```

Peewee needs you to specify the join predicate when using models that don't have a foreign-key relationship, simple as that.

Is there any reason why you wouldn't just:

```python
class ModelB(BaseModel):
  class Meta:
    table_name = 'model_b'

  model_b_id = IntegerField()  # presumably this should have primary_key=True?
  model_a = ForeignKeyField(ModelA)
```

Note that if you're using the lower-level query builder APIs these constraints do not exist and you can express a natural join:

```python
TableA = ModelA._meta.table
TableB = ModelB._meta.table
TableC = ModelC._meta.table

query = (TableA
        .select(TableA.model_a_id, TableB.model_b_id, TableC.model_c_id)
        .join(TableB, JOIN.NATURAL)
        .join(TableC, JOIN.NATURAL))
```

The example given is a dumbed-down version of my actual database to demonstrate the issue. In reality there are 2-3 fields required to perform the joins using compound primary keys. These compound keys serve as the mechanism for natural joins in the existing database structure, a better example would have been:

```python
from peewee import (JOIN, CompositeKey, DateField, DateTimeField, IntegerField,
                    Model, PostgresqlDatabase, TextField)

db = PostgresqlDatabase('test_db')

class BaseModel(Model):
  class Meta:
    database = db

class ModelA(BaseModel):
  class Meta:
    table_name = 'model_a'
    primary_key = CompositeKey('first_identifier', 'second_identifier')

  first_identifier = IntegerField()
  second_identifier = IntegerField()
  third_reference = TextField()
  model_a_data = TextField()

class ModelB(BaseModel):
  class Meta:
    table_name = 'model_b'
    primary_key = CompositeKey('second_identifier', 'timestamp')

  second_identifier = IntegerField()
  timestamp = DateTimeField()
  third_reference = TextField()
  model_b_data = TextField()

class ModelC(BaseModel):
  class Meta:
    table_name = 'model_c'
    primary_key = CompositeKey('third_reference', 'date')

  third_reference = TextField()
  date = DateField()
  model_c_data = TextField()

with db:
  db.create_tables([ModelA, ModelB, ModelC])

# vvv works vvv
print((ModelA._meta.table
  .select(
    ModelA.model_a_data,
    ModelB.model_b_data,
    ModelC.model_c_data
  )
  .join(ModelB._meta.table, JOIN.NATURAL)
  .join(ModelC._meta.table, JOIN.NATURAL)
).execute()[0])

# vvv errors vvv
print((ModelA
  .select(
    ModelA.model_a_data,
    ModelB.model_b_data,
    ModelC.model_c_data
  )
  .join_from(ModelA, ModelB, JOIN.NATURAL)
  .join_from(ModelA, ModelC, JOIN.NATURAL)
).execute()[0])
```

Good to know I can revert to using the tables, just raised the issue because natural joins don't actually require a foreign key relationship.

I think I'm going to close this one out as partially wontfix. Peewee nominally supports natural joins, but they were added in 3.x, and to-date, Peewee still does not support the "USING ..." clause that typically goes with a natural join.

When using models, Peewee requires a join predicate -- either supplied by the user or inferred from the declared foreign-key relationships. Cartesian joins can be accomplished by specifying multiple tables in the from clause (using `.from_(Src1, Src2, ...)`).