No UID when using Django admin page to create objects - django

So I have created a custom User model and a common model as base for all other models that adds a created_by field.
If I now use the Django admin page to create an object(just for testing) then I get the error that the UID is Null.
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
"""Creates and saves a new User"""
if not email:
raise ValueError("Users must have an email address")
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
"""creates and saves a new superuser"""
user = self.create_user(email, password)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class CommonModel(models.Model):
"""Common fields that are shared among other models"""
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
editable=False,
related_name="+",
)
class User(AbstractBaseUser, PermissionsMixin):
"""Custom user model that supports using email instead of username"""
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = "email"
I get this error:
IntegrityError at /admin/core/tenant/add/
null value in column "created_by_id" of relation "core_tenant" violates not-null constraint
DETAIL: Failing row contains (3, 2022-05-20 15:11:27.755389+00, 2022-05-20 15:11:27.755413+00, test1234, blablka, null, null, null, null).
Request Method: POST
Request URL: http://127.0.0.1:8900/admin/core/tenant/add/
Django Version: 4.0.4
Exception Type: IntegrityError
Exception Value:
null value in column "created_by_id" of relation "core_tenant" violates not-null constraint
DETAIL: Failing row contains (3, 2022-05-20 15:11:27.755389+00, 2022-05-20 15:11:27.755413+00, test1234, blablka, null, null, null, null).
Exception Location: /py/lib/python3.9/site-packages/django/db/backends/utils.py, line 89, in _execute
Python Executable: /py/bin/python
Python Version: 3.9.9
Python Path:
['/fwmodule',
'/usr/local/lib/python39.zip',
'/usr/local/lib/python3.9',
'/usr/local/lib/python3.9/lib-dynload',
'/py/lib/python3.9/site-packages']
Server time: Fri, 20 May 2022 15:11:27 +0000
So it seems like when using the admin page to create objects the UID cannot be resolved.
Any ideas?

Related

Is it possible to create a superuser with different inputs than a simple user in Django Rest Framework?

I am beginning a new project with Django Rest Framework, and I have a specifical need on the creation of a user: to create an account, you need to give information such as your birthdate, the birthdate of a friend, and several other info.
But it would NOT be relevant for a superuser to give such information, that's why I am looking for a way to require different info for user and for superuser. Do you know if it's possible ?
In the file models.py, I created 2 different classes :
class UserProfile(AbstractBaseUser, PermissionsMixin)
class SuperUserProfile(AbstractBaseUser, PermissionsMixin)
These two classes require different info to create an account.
In addition, I created a class to manage user profiles :
class UserProfileManager(BaseUserManager):
"""Manager for user profiles"""
def create_user(self, name1, firstName1, email1, name2, firstName2, birthDate2, password=None):
"""Create a new user profile"""
email1 = self.normalize_email(emailParrain)
user = self.model(emailParrain=emailParrain,
name1=name1,
firstName1=firstName1,
name2=name2,
firstNameUser=firstNameUser,
birthDateUser=birthDateUser)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name, password):
"""Create and save a new superuser with given details"""
user = self.create_user(email,password)
user.is_superuser = True
user.is_staff = True
user.save(using=self._db)
return user
But when I do this, I cannot create a superuser with only the info sub-mentionned (email, name, password).
you just need to add the required fields and specifying the Manager in users models
class User (AbstractBaseUser, PermissionsMixin):
user_name = models.CharField(max_length=150, unique=True)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
...
objects = UserProfileManager()
USERNAME_FIELD = 'user_name'
#the user_name and first_name are required
REQUIRED_FIELDS = ['user_name', 'first_name']
def __str__(self):
return self.user_name

Django : Set is_active flag for User created by social-auth-app-django and google_oauth2

I am using social-auth-app-django for signup-signin of new users using google oauth2 authentication.
after signup a new user is created in my db but the is_active is set as false, I want to set is_active as true only for users created by this social_auth google authentication
(for other users who sign up using email-password I activate them by sending an account activation email)
I have tried setting is_active = True for all users with no password , but I feel this way is insecure and hackish .
How do I modify the social_auth_login flow to activate users as well ?
I am using a custom User model :
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('The Email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
if password:
user.set_password(password)
# else:
# user.is_active = True <-------- tried this , worked too
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_active', True)
extra_fields.setdefault('user_type', user_constants.SUPERUSER)
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self.create_user(email, password, **extra_fields)
..
class User(AbstractUser):
username = None # remove username field, we will use email as unique identifier
email = models.EmailField(unique=True, null=True, db_index=True)
client_id = models.UUIDField(primary_key = True,
default = uuid.uuid4,
editable = False)
name = models.CharField(max_length=255, default="")
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
user_type = models.PositiveSmallIntegerField(choices=user_constants.USER_TYPE_CHOICES, default=user_constants.CLIENT_ADMIN)
REQUIRED_FIELDS = []
USERNAME_FIELD = 'email'
objects = UserManager()
..
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.social_auth.associate_by_email',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
)
SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = True
SOCIAL_AUTH_USER_MODEL = 'register.User'
SOCIAL_AUTH_GOOGLE_OAUTH2_USER_FIELDS = ['email']
..
According to Django, the boolean is_active
designates whether this user account should be considered active. We
recommend that you set this flag to False instead of deleting
accounts; that way, if your applications have any foreign keys to
users, the foreign keys won’t break.
In your case, I would put is_active as True by default (if you want to delete an account, you just put it False).
Following your remark
(for other users who sign up using email-password I activate them by
sending an account activation email)
you can add a boolean is_email_verified : if the user is created by social auth, it means the is_email_verified is True; if the user is created following email-password, is_email_verified is False and must be set to True by sending an account activation email.
Thanks to that you can have 4 states with the 2 booleans is_active and is_email_verified : a user who wants to connect must have both of them as True. It seems secure to me.

DjangoRestFramework serializing custom user models

I have defined a custom user model class as follows
class LabUserManager(BaseUserManager):
"""
A new user manager for both standard and administrative users
"""
def create_user(
self,
email,
first_name,
last_name,
university,
job_title,
bio,
password=None
):
"""
Creates a standard user with no administrative privledges
"""
if not email:
raise ValueError('Users must provide an email')
if not first_name:
raise ValueError('Users must provide a first name')
if not last_name:
raise ValueError('Users must provide a last name')
if not university:
raise ValueError('Users must provide an email address')
# Note: a biography and job_title are not required
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(
self,
email,
first_name,
last_name,
university,
job_title,
bio,
password=None
):
"""
Creates an administrative user
"""
user = self.create_user(
self,
email,
first_name,
last_name,
university,
job_title,
bio,
password=None
)
user.is_admin = True
user.save(using=self._db)
return user
class LabUser(AbstractBaseUser):
"""
Model for every user on the site
The only required fields are:
email,
first_name,
last_name,
university,
although, this will be discussed later
"""
email = models.EmailField(
verbose_name = 'email address',
max_length = 255,
unique = True,
db_index = True,
)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
university = models.CharField(max_length=150)
job_title = models.CharField(max_length = 50)
bio = models.TextField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
objects = LabUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [
'first_name',
'last_name',
'university',
]
#property
def is_staff(self):
return self.is_admin
With the serializer
class NewUserSerializer(serializers.Serializer):
"""
Defines a new user serializer
"""
pk = serializers.Field()
email = serializers.EmailField()
first_name = serializers.CharField(max_length=50)
last_name = serializers.CharField(max_length=50)
university = serializers.CharField(max_length=150)
job_title = serializers.CharField(
max_length=50,
required=False
)
bio = serializers.CharField(
widget=widgets.Textarea,
required=False
)
password = serializers.CharField(
max_length=64,
widget=widgets.PasswordInput
)
def restore_object(self, attrs, instance=None):
if instance: # Update email university bio or job_title
user = instance
user.email = attrs['email']
user.university = attrs['university']
user.bio = attrs['bio']
user.job_title = attrs['job_title']
user.set_password(attrs.get('password'))
else:
user = LabUser(
email=attrs['email'],
first_name=attrs['first_name'],
last_name=attrs['last_name'],
university=attrs['university'],
job_title=attrs['job_title'],
bio=attrs['bio']
# Check custom user model page for other parameters
)
user.set_password(attrs.get('password'))
user.save()
return user
with the View
class NewUser(generics.CreateAPIView):
serializer_class = NewUserSerializer
But when I post a new user with a new email address, I receive the following error
IntegrityError at /users/new/
PRIMARY KEY must be unique
Request Method: POST
Request URL: http://127.0.0.1:8000/users/new/
Django Version: 1.5.1
Exception Type: IntegrityError
Exception Value:
PRIMARY KEY must be unique
At some point, you seem to submit and process a primary key value when creating new objects. It's only a guess, but I think this happens somewhere in your NewUserSerializer because you explicitly added a pk field, that is not read only and does not enforce a positive integer value. I would try using an IntegerField with read_only=True instead and see if that fixes the problem.
But I strongly recommend using ModelSerializer anyway. You could still have your custom widgets for each field, but it would also handle a few things a little more concise than your implementation does. And you'd have to write much less code.

Can't create super user with custom user model in Django 1.5

my goal is to create a custom user model in Django 1.5
# myapp.models.py
from django.contrib.auth.models import AbstractBaseUser
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
db_index=True,
)
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
company = models.ForeignKey('Company')
...
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['company']
I can't create a super user because of the company field (models.ForeignKey('Company') (python manage.py createsuperuser).
My question:
How can I create a super user for my application without a company.
I tried to make a custom MyUserManager without any success:
class MyUserManager(BaseUserManager):
...
def create_superuser(self, email, company=None, password):
"""
Creates and saves a superuser with the given email and password.
"""
user = self.create_user(
email,
password=password,
)
user.save(using=self._db)
return user
Or do I have to create a fake company for this user?
Thank you
There are three ways for you in this case
1) Make relation to company Not required company = models.ForeignKey('Company', null=True)
2) Add default company and provide it as default value to foreign key field company = models.ForeignKey('Company', default=1) #where 1 is id of created company
3) Leave model code as is. Add fake company for superuser named for example 'Superusercompany'
set it in create_superuser method.
UPD: according to your comment way #3 would be the best solution not to break your business logic.
Thanks to your feedback here is the solution I made:
A custom MyUserManager where I created a default company
def create_superuser(self, email, password, company=None):
"""
Creates and saves a superuser with the given email and password.
"""
if not company:
company = Company(
name="...",
address="...",
code="...",
city="..."
)
company.save()
user = self.create_user(
email,
password=password,
company=company
)
user.is_admin = True
user.save(using=self._db)
return user

Create user inactive as default (is_active default False)

I have facebook authentication in my website which I use omab / django-social-auth
I want to redirect users to another website to make them fill their details. So I want to have users as inactive when they first authenticate with their facebook accounts, then after they complete the form I save them as active users.
I manipulated the django/contrib/auth/models.py under my enviroment as with is_active fields as default=False; but they are saved as active user
but still the same result, even I add a normal user from the admin panel. Is there something I am missing?
class User(models.Model):
"""
Users within the Django authentication system are represented by this
model.
Username and password are required. Other fields are optional.
"""
username = models.CharField(_('username'), max_length=30, unique=True,
help_text=_('Required. 30 characters or fewer. Letters, numbers and '
'#/./+/-/_ characters'))
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('e-mail address'), blank=True)
password = models.CharField(_('password'), max_length=128)
is_staff = models.BooleanField(_('staff status'), default=False,
help_text=_('Designates whether the user can log into this admin '
'site.'))
is_active = models.BooleanField(_('active'), default=False,
help_text=_('Designates whether this user should be treated as '
'active. Unselect this instead of deleting accounts.'))
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
last_login = models.DateTimeField(_('last login'), default=timezone.now)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'))
user_permissions = models.ManyToManyField(Permission,
verbose_name=_('user permissions'), blank=True,
help_text='Specific permissions for this user.')
objects = UserManager()
def create_user(self, username, email=None, password=None):
"""
Creates and saves a User with the given username, email and password.
"""
now = timezone.now()
if not username:
raise ValueError('The given username must be set')
email = UserManager.normalize_email(email)
user = self.model(username=username, email=email,
is_staff=False, is_active=False, is_superuser=False,
last_login=now, date_joined=now)
user.set_password(password)
user.save(using=self._db)
return user
Avoid modifying built-ins. There are better ways to do things.
Like signals. Signals are awesome.
In this case, I'd attach to the pre_save signal of django.contrib.auth.models.User, and manually correct the is_active property of the model instance (if the object is new).
This way, you can add some logic to make sure you're properly marking a user as not active.
Because user's added within the Admin should probably be active, if the admin marks them as active.
jack_shed suggested signals, which helped me find the direction to take it. But there was still work from there to figure out how exactly to test and modify after receiving the signal.
Here's what worked for me.
from django.dispatch import receiver
from django.db.models.signals import pre_save
from django.contrib.auth.models import User
#receiver(pre_save, sender=User)
def set_new_user_inactive(sender, instance, **kwargs):
if instance._state.adding is True:
print("Creating Inactive User")
instance.is_active = False
else:
print("Updating User Record")
This will catch the action of creating a user before the save occurs, then test if this instance state is "adding" or not. That differentiates between creating and updating a model instance.
If you don't do this test, updating the user sets is_active to False also, and there ends up being no way to activate them through django.
Elegant solution when using django-allauth.
There is another very nice solution.. one that sounds very much like what you desire.
I have created a custom form (in my case a ModelForm) that I can hand over to django-allauth via the ACCOUNT_SIGNUP_FORM_CLASS setting. What this does.. is ask the new potential user to supply additional fields during the signup process.
That has some very nice advantages:
You can add some fields very elegantly in addition to the default stuff.
It works for both social and "normal" signup.
No patching of 3rd party apps required.
You are still able to modify and maintain everything in the admin.
In the custom form you get access to the new user instance before it gets saved to the
database. This means you can even process the provided information do things like create a profile object for him and set the user as inactive ..all in one go.
This works because you can check if everything is ok.. and only then commit to do all these steps or reject the form with a validation error. :)
Well.. sounds good right?
But how exactly does it work (i.e. look like)?
I am glad you asked.. ^_^
For your use case it might look something like this:
settings.py
[...]
ACCOUNT_SIGNUP_FORM_CLASS = "<your_app>.forms.SignupForm"
[...]
forms.py
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.is_active = False
user.save()
I think a better approach would be to customize your own model manager by leaving the user value inactive as default.
Create in your app: project_app/managers.py:
# project_app/managers.py
from django.contrib.auth.base_user import BaseUserManager
class UserManager(BaseUserManager):
def create_user(self, username, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
extra_fields.setdefault('is_active', False)
return self._create_user(username, email, password, **extra_fields)
and in you models:
#project_app/models.py
from .managers import UserManager
from django.contrib.auth.models import AbstractUser
class UserManager(AbstractUser):
objects = UserManager()
# your custom fields go here.