Django Admin Create Form Inline OneToOne - python

I have the following model:
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
last_password_reset = models.DateTimeField(auto_now_add=True)
needs_password_reset = models.BooleanField(default=True)
image_url = models.URLField(max_length=500, default=None, null=True, blank=True)
I am trying to inline this into the admin. I have the following:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm
class UserProfileInline(admin.StackedInline):
"""User profile inline."""
model = Profile
can_delete = False
verbose_name_plural = "Profile"
class CustomUserCreationForm(UserCreationForm):
"""Create user form."""
class Meta:
model = User
fields = ("username", "first_name", "last_name", "email")
class CustomUserAdmin(UserAdmin):
"""Custom user admin."""
add_form = CustomUserCreationForm
inlines = (UserProfileInline,)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
This is working fine up to a point; when I go to create user, I can see the inlined profile information. However, when I try to submit the form I get the following error (on /admin/auth/user/add/):
psycopg2.errors.NotNullViolation: null value in column "user_id" violates not-null constraint
Why is the user_id field not getting populated in the inline form? How can I set this attribute to the id of the user created by the form?

Turns out I needed to remove some signals I had written for automatic profile creation:
# Create a profile for new users
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_user_profile(sender, instance=None, created=False, **kwargs):
if created:
Profile.objects.get_or_create(user=instance)
# Update profile on user change
#receiver(post_save, sender=User)
def save_user_profile(sender, instance=None, created=False, **kwargs):
instance.profile.save()

Related

Bulk user import in Django

I like to bulk import users from xlsx or csv to my original User model. I don't use AbstractUser or AbstractBaseUser and I don't like to because my site is working and I don't want to abuse my schema.
I am using the original User model where I add the users (no user registration allowed) and I store the extra fields in my Profile model with a OneToOne relation.
I'm not so experienced so I tried to use code snippets that I found but I am still not able to achieve my goal. If I try to use import-export module in my admin panel it works with other models but not with the User model. Tablib or other solutions would be also interesting to me.
models.py
(I'm using the original User model that sends a signal to Profile model when a user is created)
class Profile(models.Model):
def __str__(self):
return str(self.user)
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True, auto_now=False, blank=True)
projekt = models.ForeignKey(Projekt, on_delete=models.CASCADE, default=1)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
forms.py
class RegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'last_name', 'first_name', 'password1', 'password2']
admin.py
from django.contrib.auth.models import User
from import_export.admin import ImportExportModelAdmin
#admin.register(User)
class UserAdmin(ImportExportModelAdmin):
pass
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
I got this error message:
django.contrib.admin.sites.AlreadyRegistered: The model User is already registered with 'auth.UserAdmin'.
I understand that my User model is in use but what could I do?

i want to add custom field in django-allauth SignupForm

i wanted to add custom field with django-allauth SingupForm and adding new field like phone number. i already managed to add this field in Postgresql on my own(without migrations,but by my hands).
this is my postgresql screen
In my signup page i have these fields already but i can't managed to add "phone" to my database, i really want to make it! please someone help me.
forms.py
from allauth.account.forms import SignupForm
from django import forms
class CustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=30, label='Voornaam')
last_name = forms.CharField(max_length=30, label='Achternaam')
phone = forms.CharField(max_length=30, label='phone')
def __init__(self, *args, **kwargs):
super(CustomSignupForm, self).__init__(*args, **kwargs)
self.fields['first_name'] = forms.CharField(required=True)
self.fields['last_name'] = forms.CharField(required=True)
self.fields['phone'] = forms.CharField(required=True)
def save(self, request):
user = super(CustomSignupForm, self).save(request)
user.phone = self.cleaned_data.get('phone')
user.save()
return user
def signup(self,request,user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return user
settings.py
ACCOUNT_FORMS = {'signup': 'registration.forms.CustomSignupForm'}
What you need is a profile model which is attached to a user so that you can add extra fields for information you might want.
If you're starting a project from the very beginning you can also consider a custom user model so that all data is on one object.
When I do this, I create an accounts app which I put my overrides in for allauth and my model starts something like this (modified to add the receiver function which I don't have at the moment because I'm not using Profile objects);
from django.contrib.auth import get_user_model
from django.db import models
from django.utils.translation import ugettext_lazy as _
User = get_user_model()
class Profile(models.Model):
"""
Profile model
"""
class Meta:
"""
Metadata
"""
app_label = 'accounts'
verbose_name = _('User profile')
verbose_name_plural = _('User profiles')
user = models.OneToOneField(
verbose_name=_('User'),
to=User,
related_name='profile',
on_delete=models.CASCADE
)
# Add your fields here like `phone`
def __str__(self):
"""
String representation
"""
return f'User Profile for: {self.user}'
#receiver(post_save, sender=User)
def create_profile(sender, instance, **kwargs):
"""
Setup a profile as a user is created
"""
Profile.objects.create(user=instance) # Using `create` also saves the object
Your signup form then does something like
def save(self, request):
user = super(CustomSignupForm, self).save(request)
user.profile.phone = self.cleaned_data.get('phone')
user.profile.save()
return user
If you've already got users you'll also need a migration which creates profiles for them;
# Generated by Django 2.2.12 on 2020-05-01 22:03
from django.db import migrations
def create_profiles(apps, schema_editor):
User = apps.get_model('authentication', 'User') # this should match the User model you are using
Profile = apps.get_model('accounts', 'Profile')
for user in User.objects.all():
profile = Profile.objects.create(user=user)
profile.save()
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.RunPython(create_profiles, migrations.RunPython.noop)
]

How to select a OnetoOne field from a QuerySet in a view?

See all users (that don't have search_hidden enabled) view
#login_required
def users(request):
"""List all users page"""
t_users = User.objects.all()
users = t_users.usersettings.filter(search_hidden=False).select_related('user')
context = {'users': users}
return render(request, 'users/users.html', context)
UserSettings model
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class UserSettings(models.Model):
"""Stores the user's settings."""
user = models.OneToOneField(User, related_name='usersettings', on_delete=models.CASCADE)
public_profile = models.BooleanField(default=True)
search_hidden = models.BooleanField(default=False)
class Meta:
verbose_name_plural = 'usersettings'
def __str__(self):
return f"{self.user}'s settings"
#receiver(post_save, sender=User)
def create_user_usersettings(sender, instance, created, **kwargs):
if created:
UserSettings.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_usersettings(sender, instance, **kwargs):
instance.usersettings.save()
All users have a UserSettings model tied to them when their accounts are created. In my view, I want to select all users that have search_hidden disabled, however what I've tried doesn't work.The error 'QuerySet' object has no attribute 'usersettings' is displayed whenever the page is requested. I probably need to select each user and retrieve the settings, but I don't know how to do that in an efficient manner.
iliya commented that filtering using t_users.objects.filter(search_hidden=False) would return users where search_hidden is not true in their settings object.

Django: authtools do create the user profile when the user is created

I am using the authtools plugin to manage the user and its profile in django, but when I create the user, it does not create its profile, I have to go to the admin part of the site and create it manually.
I separated the applications into account and profile.
This is the profiles model:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
primary_key=True)
slug = models.UUIDField(default=uuid.uuid4, blank=True, editable=False)
email_verified = models.BooleanField("Email verified", default=True)
This is the signal.py, that is inside of the profiles application:
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_handler(sender, instance, created, **kwargs):
if not created:
return
profile = models.Profile(user=instance)
profile.save()
logger.info('New user profile for {} created'.format(instance))
This is the admin.py of the account app:
class UserProfileInline(admin.StackedInline):
model = Profile
class NewUserAdmin(NamedUserAdmin):
inlines = [UserProfileInline]
list_display = ('is_active', 'email', 'name', 'permalink',
'is_superuser', 'is_staff',)
# 'View on site' didn't work since the original User model needs to
# have get_absolute_url defined. So showing on the list display
# was a workaround.
def permalink(self, obj):
url = reverse("profiles:show",
kwargs={"slug": obj.profile.slug})
# Unicode hex b6 is the Pilcrow sign
return format_html('{}'.format(url, '\xb6'))
admin.site.unregister(User)
admin.site.register(User, NewUserAdmin)
admin.site.register(Profile)
When I signup a user, both the user and the profile objects are created, only they are not linked. Why is that?
Thank you
Use this below your profile model in models.py. I hope you are generating slug by another slugify signal.
def user_created_receiver(sender, instance, created, *args, **kwargs):
if created:
Profile.objects.get_or_create(user = instance)
post_save.connect(user_created_receiver, sender = User)

Custom column in Django users profile

I want to add custom column in Django admin, on Users (/auth/user/) section.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birthday = models.DateField()
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
And in admin.py I have this code:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'Custom fields'
class CustomUser(UserAdmin):
inlines = (ProfileInline, )
list_diplsay = ('birthday')
def get_inline_instances(self, request, obj=None):
if not obj:
return list()
return super(CustomUser, self).get_inline_instances(request, obj)
admin.site.unregister(User)
admin.site.register(User, CustomUser)
I've read here that list_display should do all the work, but in my case it doesn't work. I don't see any changes in my admin panel since I've added that line.
Where is the problem? Thanks!
Edit: Fixed by changing list_diplsay with list_display. Now I get this: type object 'User' has no attribute 'birthday'. Any ideas?
You have a typo: list_diplsay should be list_display. You should probably also add a trailing comma to your list value: ('birthday',). This ensures you end up with a tuple and not a single value.
Edit:
Since the birthday field doesn't belong to the User you'll have to add a method to look it up in your admin class:
class CustomUser(UserAdmin):
list_display = ('birthday',)
def birthday(self, obj):
return obj.profile.birthday
(I have only shown the relevant parts of the admin class; your existing inlines etc. should stay.)

Categories

Resources