Home > Enterprise >  Django - How to set ForeignKey to a model that itself has a one-to-one relationship to User?
Django - How to set ForeignKey to a model that itself has a one-to-one relationship to User?

Time:02-13

Here's my class:

from django.db import models
from django.contrib.auth.models import User


class CredsKeychain(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    
    def __str__(self) -> str:
        return f"{self.user.username}'s credentials"


class CredKey(models.Model):
    keychain = models.ForeignKey(CredsKeychain, on_delete=models.CASCADE)
    tag = models.CharField(max_length=100)
    username = models.CharField(max_length=100)
    enc_password = models.CharField(max_length=100)
    last_used_successfully = models.DateTimeField(default=None)
    
    def __str__(self) -> str:
        return f"{self.keychain.user.username}'s credentials for {self.tag} [{self.username}]"

I'm trying to have the relationship set up such that each user has a CredsKeychain that has multiple CredKey objects. I'm having a hard time trying to reference CredsKeychain from CredKey. I can directly link the user, via:

keychain = models.ForeignKey(User, on_delete=models.CASCADE)

But that's not what I need. I actually need to have a ForeignKey relationship to the CredsKeychain that has a one-to-one relationship with the current user (who submits the page in Django admin).

When I try to migrate this model (in its current state), I get the error:

It is impossible to add a non-nullable field 'keychain' to credkey without specifying a default. This is because the database needs something to populate existing rows. Please select a fix:

  1. Provide a one-off default now (will be set on all existing rows with a null value for this column)
  2. Quit and manually define a default value in models.py.

How can I reference the user.credskeychain?


Update: I already have a signal setup so that everytime a user is created, it automatically creates a CredsKeychain for that user.

@receiver(post_save, sender=User)
def setup_keychain(sender, instance:User, created, **kwargs):
    if created: 
        CredsKeychain.objects.create(user = instance)

Update 2: Model works just fine as-is once I dropped the DB and deleted the old migrations (nuked the DB). Now that I start again, it migrates just fine. So what was the problem earlier?!

(env) PS C:\Users\somsinha\scripts\djpu-keystore> python manage.py makemigrations
Migrations for 'keystore':
  keystore\migrations\0001_initial.py
    - Create model CredsKeychain
    - Create model CredKey
(env) PS C:\Users\somsinha\scripts\djpu-keystore> python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, keystore, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying keystore.0001_initial... OK
  Applying sessions.0001_initial... OK

CodePudding user response:

if you want to add any new fields to your models make sure that you don't have any previously added records in the DB. if you have any records you should out some default values in the model field. because when you try to migrate new fields without any default value what should DB do ? how should it fill for the previous records? so there is 2 way to fix it as it says:

  1. Provide a one-off default now (will be set on all existing rows with a null value for this column)
  2. Quit and manually define a default value in models.py.

so you can select option 1 and write a python command to fill the previous records or select option 2 and define the default value in the model field manually

  • Related