Search code examples
pythondjangodatabaseclassmodel

How to assign a user to a model in my database


    from django.db import models
    from datetime import datetime
    from django.contrib.auth import get_user_model

    User = get_user_model()


    class Blog(models.Model): 
        user = models.ForeignKey(User, on_delete=models.CASCADE)
        headline = models.CharField(max_length=250)
        content = models.CharField(max_length=2050)
        time_created = models.DateTimeField(default=datetime.now, blank=True)

        def __str__(self):
            return self.user.username

every time I migrate this

"(venv) PS C:\Users\user\Desktop\APPS\web_app_project> python manage.py makemigrations"

I always get this message:

"It is impossible to add a non-nullable field 'user' to blog 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.

Select an option:"

How do I go about this


Solution

  • Because you've added the non-nullable field user to Blog Django needs to add a user to all instances of blogs in the database, both new ones and existing. If you've created a blog instance in the database, what should Django do with its new user column? That's what it is asking you.

    Only if there is no data in the database or you are completely OK with losing data, you can migrate your app to zero with python manage.py migrate <your app name> zero (You might want to reverse to migration besides zero. You can read more about reverse migrations). This will effectively undo all of your migrations for that app. You can then delete the existing migrations for that app and run makemigrations again. Django will no longer complain about the non-nullable field user, as this results in a migration that creates a Blog table with a user field, instead of a migration that attempts to add a user field to an existing Blog table. Once again, do not do this unless you are OK with losing data. This should never be done if your app is already running in production, but it is OK if you have never deployed the app and have no "real" data, and you are still in the initial development phase. Also, make sure you have a backup of deleted migrations in case you need to add them back.

    As others have suggested, you can create a default user model that is used as the one-time default to add users to Blogs. For example (in Django shell)

    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    user = User(username='default_blog_user')
    user.set_unusable_password() # Nobody should be able to log in as this user
    user.save()
    print(user.id) # keep this ID
    

    Then, in the migration, you can use whatever that user.id value was as the one-time-default. But this once again assumes that you haven't deployed to production, as the one-time-default and the IDs in development and production may not match.

    If you have already deployed to production, I think the only thing you can do is make the user field nullable for the sake of your migration, but assert that it is not null in your programming logic. For example, by adding a validator to the field.


    Side note: instead of running get_user_model in your models module, you should do this:

    from django.conf import settings
    
    class Blog(models.Model):
        user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
        # etc.
    

    When you define a foreign key or many-to-many relations to the user model, you should specify the custom model using the AUTH_USER_MODEL setting.

    source