Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Model OneToOneField without creating additional _id database column

I'm working with an Account model and I want to inner join it with a Settings model without having to create an additional settings_id column in my Account model, because the PK on the Account table matches the PK on the settings table exactly. I've been trying to set up a OneToOne relationship between these tables & columns, but can't figure out how to do that without having to create a new column or something. I'm trying to work with the existing schema as much as possible and would prefer to not create unnecessary columns.

Class Account(models.Model):
    login_id = models.AutoField(primary_key=True)
    settings = models.OneToOneField('Settings', to_field='login_id', null=True,
        related_name='settings', db_column='login_id') # Doesn't work because login_id already exists....


Class Settings(model.Model):
    login_id = models.OneToOneField('Account', to_field='login_id', related_name='account')

Basically, the query I'm trying to replicate is:

SELECT * FROM account
INNER JOIN settings
ON account.login_id=settings.login_id
WHERE account.login_id=1

Based on the errors, Django's ORM seems really persistent on creating a new column in the Account table, but I don't see the point of adding a new column when the relationship between the 2 tables is so simple: account.login_id = settings.login_id

like image 446
Cory Danielson Avatar asked Oct 04 '14 02:10

Cory Danielson


People also ask

Does Django support multiple column primary keys?

Do Django models support multiple-column primary keys? ¶ No. Only single-column primary keys are supported.

Can a Django model have multiple foreign keys?

Your intermediate model must contain one - and only one - foreign key to the source model (this would be Group in our example), or you must explicitly specify the foreign keys Django should use for the relationship using ManyToManyField.

Does Django automatically create ID?

Django will create or use an autoincrement column named id by default, which is the same as your legacy column.

Does Django create ID field?

By default, Django adds an id field to each model, which is used as the primary key for that model. You can create your own primary key field by adding the keyword arg primary_key=True to a field. If you add your own primary key field, the automatic one will not be added.


2 Answers

Although I've never tried it myself, declaring the OneToOneField as the primary key should work.

class Account(models.Model):
    login_id = models.AutoField(primary_key=True)

class Settings(model.Model):
    account = models.OneToOneField('Account', to_field='login_id', 
        primary_key=True, related_name='settings')

Declaring the primary_key will keep Django from trying to create a primary key column for you, and with this setup you can access account.settings and settings.account or settings.account_id.

like image 150
Kevin Christopher Henry Avatar answered Oct 16 '22 10:10

Kevin Christopher Henry


Here's what I ended up doing:

class Account(models.Model):
    login_id = models.AutoField(primary_key=True)

    def get_settings_dict(self):
        try:
            return self.settings.to_dict()
        except Settings.DoesNotExist:
            return Settings.get_defaults_dict()

class Settings(model.Model):
    account = models.OneToOneField('Account', to_field='login_id', related_name='settings')
    # other settings properties
    # other settings properties

    def to_dict(self):
        # return current values as dict

    @staticmethod
    def get_defaults_dict():
        # return default values dict

This allowed me to access a user's settings via account.settings without having to create an additional settings_id column on the Account model. The OneToOne relationship wasn't needed on both models.

Also, the get_settings_dict method returns the default settings when the settings do not exist yet for a user. This is a nice convenience that prevents me from having to do a null check every time I access the user's settings via account.settings.

like image 26
Cory Danielson Avatar answered Oct 16 '22 12:10

Cory Danielson