Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Join and query Django models on non-primary-key relationship?

I've got two models that are logically related through a field that is not the primary key. Is it possible to query them (ex, select_related(…)) without introducing a ForeignKey column?

For example, consider the contrived models:

class LogEntry(Model):
    source_name = CharField(…)
    log_message = CharField(…)

class LogSource(Model):
    name = CharField(…)
    domain = CharField(…)

I would like to be able to query LogEntry, joining in and filtering on the related LogSource (ex, so I can access log_entry.source without additional queries):

LogEntry.objects
    .select_related(
        source=Join(LogSource, on="logsource.name = logentry.source_name")),
    )
    .filter(source__domain="example.com")

Is this possible without introducing a ForeignKey?

like image 268
David Wolever Avatar asked Jul 30 '15 23:07

David Wolever


People also ask

Does Django model need primary key?

Primary Keys Every table should have a primary key, so every model should have a primary key field. However, you do not have to do this manually if you do not want. By default, Django adds an id field to each model, which is used as the primary key for that model.

How do I merge two Django models?

The SolutionThe Python union operator can be used to combine QuerySets that belong to the same model. You can also use the chain() method from the Itertools module, which allows you to combine two or more QuerySets from different models through concatenation.

How does Django handle one to many relationships?

To handle One-To-Many relationships in Django you need to use ForeignKey . The current structure in your example allows each Dude to have one number, and each number to belong to multiple Dudes (same with Business).

Can a Django model have more than one foreign key?

Models can have multiple foreign keys.


1 Answers

You should be able to do this by using extra() with the tables option.

LogEntry.objects.extra(
    tables=['logsource'],
    where=['logsource.name=logentry.source_name',
           'logsource_domain="example.com"',
           ]
)

Another option is to change source_name to a foreign key, but specify the db_column and to_field arguments to use the existing columns. I know that you said that you didn't want to add a foreign key, but it might be acceptable because it only changes the models, not the columns in the database tables. However, be aware that Django might want to create a foreign key constraint. One hack would be to fake that migration so that the constraint isn't created in the db.

class LogEntry(Model):
    source_name = models.ForeignKey(db_column=source_name', to_field='name')

log_entry.source_name would then be the LogSource instance, and log_entry.source_name_id would be the value stored in the source_name column. It might make sense to rename the field from source_name to source after converting to a foreign key, but that's not necessary.

like image 147
Alasdair Avatar answered Oct 15 '22 03:10

Alasdair