2

I have a Profile model that extends the user model like so,

class Profile(User):
    user = models.OneToOneField(User, parent_link=True, on_delete=models.CASCADE)
    slug = models.SlugField(unique=True, blank=True)
    def save(self, *args, **kwargs):
        print('self.username')
        print(self.username)
        self.slug = self.username
        super(Profile, self).save(*args, **kwargs)

I am trying to create a slug field for my model , so I override the save method to include the slug as the username. The thing is, when I create a new user with the command createsuperuser and print out the username as you can see in the code, it doesn't show anything - it doesn't show the provided username. Could this be the reason why I am having this problem ? And if so, how can I fix it?

ayePete
  • 411
  • 1
  • 7
  • 23
A. S.
  • 143
  • 2
  • 12

3 Answers3

2

If you're sure you don't want to have your user's profile data in a separate model with a OneToOneField back to Django's default User, then you should probably subclass AbstractUser and not User, as specified in the docs.

https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#substituting-a-custom-user-model

Consider this part :

If you’re entirely happy with Django’s User model and you just want to add some additional profile information, you could simply subclass django.contrib.auth.models.AbstractUser and add your custom profile field [...]

(from https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#extending-django-s-default-user)

Then you'd go like this:

from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import UserManager
from django.utils.text import slugify
from django.db import models


class User(AbstractUser):
    slug = models.SlugField(unique=True, blank=True)

    objects = UserManager()

    def save(self, *args, **kwargs):
        print('self.username')
        print(self.username)
        self.slug = slugify(self.username)
        super().save(*args, **kwargs)

Then, define settings.AUTH_USER_MODEL,

AUTH_USER_MODEL = "myapp.User"  # also, add 'myapp' to INSTALLED_APPS

Remember: you'll need to reference your User model like this (and this is how you should do it anyway, whether you customized that model or not):

from django.contrib.auth import get_user_model

User = get_user_model()
daragua
  • 1,133
  • 6
  • 8
  • Thanks a lot this one worked for me , I wonder why the documentation is not as clear as your answer is , for example it doesn't mention neither the `get_user_model` thing nor the `objects= UserManager() ` thing, I can't express how thankful I am though – A. S. Nov 17 '19 at 20:34
  • I mean where can I find such information if I need to ? – A. S. Nov 17 '19 at 21:02
  • Actually, everything is in the documentation. Django has a very complete doc, but it does take time to find your way through it. I usually skim though it, noting mentally that there's a chapter about this or that topic, then I go back. Or simply by googling :) – daragua Nov 17 '19 at 22:12
  • Do I have to use signals to relate the new customized User to the original User model? because I used to use signals with my Profile model to correspondingly be created with the User model , and now with these changes , I still have my signals and still naming the customized user Profile and the app is working just fine and I don't understand why, shouldn't there be a problem or something ? – A. S. Nov 17 '19 at 22:49
  • Nope, the original `User` model (the Django one) will not be used anymore, it has been replaced by by the `AUTH_USER_MODEL = "myapp.User"` setting. It _is_ the model that will be used for users throughout your application. However, if you had a production database and introduced that change, you'll have to do a careful data migration yourself. – daragua Nov 18 '19 at 12:49
1

Profile cannot inherit User but instead should inherit Model. And, for the creation of User to be able to create a corresponding row in a different table (Profile) and set the slug will involve the use of signals.

mazurekt
  • 193
  • 2
  • 10
0

You are overriding the save method correctly. You need to do more when you use the createsuperuser command.

from django.utils.text import slugify
from django.contrib.auth.models import UserManager


class CustomUserManager(UserManager):
    def _create_user(self, username, email, password, **extra_fields):
        username_slug = slugify(username)
        extra_fields.setdefault('slug', username_slug)
        super()._create_user(username, email, password, **extra_fields)


class Profile(User):
    user = models.OneToOneField(User, parent_link=True, on_delete=models.CASCADE)
    slug = models.SlugField(unique=True, blank=True)

    def save(self, *args, **kwargs):
        print('self.username')
        print(self.username)
        self.slug = slugify(self.username)
        super().save(*args, **kwargs)

    objects = CustomUserManager()

mazurekt
  • 193
  • 2
  • 10
  • unfortunately the user is still stored with no username – A. S. Nov 17 '19 at 11:17
  • I have now tested it and it works for me. Any new user you create (whether with the createsuperuser command or by calling profile_obj.save() will automatically generate a slug based on the username.) – mazurekt Nov 17 '19 at 18:51
  • 1
    I now realize that you do not want this Profile to be your User model. I tested this with the assumption that it was going to be equivalent to a User model but with a different name. – mazurekt Nov 17 '19 at 20:42