Django admin - stackedInline single instance - django

I'm building a site based on a highly customized django admin instance and am running into issues with user profiles as an inline to user_admin
long story short regardless of what I set for max_num and extra in the admin.StackedInline instance it allows up to 2 profiles per user - with a blank one in place by default if the user has an existing profile
anyone know how I could adjust this to show only a single inline profile without resorting to some JS front end hack?
relevant code from: profiles.admin.py
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from profile.models import user_profile
class user_profile_admin(admin.StackedInline):
model = user_profile
fk_name = 'user'
max_num = 1
extra = 0
class user_admin_extended(UserAdmin):
inlines = [user_profile_admin, ]
admin.site.unregister(User)
admin.site.register(User, user_admin_extended)

I assume you're using FK field to connect user and profile? Try OneToOneField it should render just one inline in admin.

Related

Extending User profile in Django 1.7

I know, this question has been already asked many times in SO, but most of the answers I read were either outdated (advising the now deprecated AUTH__PROFILE_MODULE method), or were lacking of a concrete example.
So, I read the Django documentation [1,2], but I lack a real example on how to use it properly.
In fact, my problem comes when a new user is created (or updated) through a form. The user is obviously created but, the fields from the extension are all unset. I know that the Django documentation is stating that:
These profile models are not special in any way - they are just Django models that happen to have a one-to-one link with a User model. As such, they do not get auto created when a user is created, but a django.db.models.signals.post_save could be used to create or update related models as appropriate.
But, I don't know how to do it in practice (should I add a a receiver and if 'yes', which one).
For now, I have the following (taken from the documentation for the sake of brevity):
File models.py
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=100)
File admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import Employee
# Define an inline admin descriptor for Employee model
class EmployeeInline(admin.StackedInline):
model = Employee
can_delete = False
verbose_name_plural = 'employee'
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (EmployeeInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
File forms.py
class SignupForm(account.forms.SignupForm):
department = forms.CharField(label="Department", max_length=100)
class SettingsForm(account.forms.SignupForm):
department = forms.CharField(label="Department", max_length=100)
Then, in my code, I use it like this:
u = User.objects.get(username='fsmith')
freds_department = u.employee.department
But, Signup and Settings forms do not operates as expected and new values for the departement is not recorded.
Any hint is welcome !
I have looked at all the answers but none does really hold the solution for my problem (though some of you gave me quite good hints for looking in the right direction). I will summarize here the solution I have found to solve my problem.
First of all, I have to admit I didn't tell everything about my problem. I wanted to insert extra fields in the User model and use other apps such as the default authentication scheme of Django. So, extending the default User by inheritance and setting AUTH_USER_MODEL was a problem because the other Django applications were stopping to work properly (I believe they didn't use user = models.OneToOneField(settings.AUTH_USER_MODEL) but user = models.OneToOneField(User)).
As, it would have been too long to rewrite properly the other applications I am using, I decided to add this extra field through a One-to-One field. But, the documentation miss several points that I would like to fill in the following.
So, here is a complete example of adding an extra field to the User model with other applications using the same model.
First, write the description of the model gathering the extra fields that you want to add to your models.py file:
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
extra_field = models.CharField(max_length=100)
Then, we need to trigger the addition of an object UserProfile each time a User is created. This is done through attaching this code to the proper signal in the receiver.py file:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from my_user_profile_app.models import UserProfile
#receiver(post_save, sender=User)
def handle_user_save(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
Now, if you want to be able to modify it through the administration interface, just stack it with the usual UserAdmin form in the admin.py file.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import UserProfile
# Define an inline admin descriptor for UserProfile model
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (UserProfileInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Then, it is time now to try to mix this extra field with the default Django authentication application. For this, we need to add an extra field to fill in the SignupForm and the SettingsForm through inheritance in the forms.py file:
import account.forms
from django import forms
class SignupForm(account.forms.SignupForm):
extra_field = forms.CharField(label="Extra Field", max_length=100)
class SettingsForm(account.forms.SignupForm):
extra_field = forms.CharField(label="Extra Field", max_length=100)
And, we also need to add some code to display and get properly the data that you have been added to the original User model. This is done through inheritance onto the SignupView and the SettingsView views in the views.py file:
import account.views
from my_user_profile_app.forms import Settings, SignupForm
from my_user_profile_app.models import UserProfile
class SettingsView(account.views.SettingsView):
form_class = SettingsForm
def get_initial(self):
initial = super(SettingsView, self).get_initial()
initial["extra_field"] = self.request.user.extra_field
return initial
def update_settings(self, form):
super(SettingsView, self).update_settings(form)
profile = self.request.user.userprofile
profile.extra_field = form_cleaned_data['extra_field']
profile.save()
class SignupView(account.views.SignupView):
form_class = SignupForm
def after_signup(self, form):
profile = self.created_user.userprofile
profile.extra_field = form_cleaned_data['extra_field']
profile.save()
super(SignupView, self).after_signup(form)
Once everything is in place, it should work nicely (hopefully).
I struggled with this topic for about a year off and on until I finally found a solution I was happy with, and I know exactly what you mean by "there is a lot out there, but it doesn't work". I had tried extending the User model in different ways, I had tried the UserProfile method, and some other 1-off solutions as well.
I finally figured out how to simply extend the AbstractUser class to create my custom user model which has been a great solution for many of my projects.
So, let me clarify one of your comments above, you really shouldn't be creating a link between 2 models, the generally accepted "best" solution is to have one model which is inherited from AbstractUser or AbstractBaseUser depending on your needs.
One tricky thing that got me was that "Extending the User Model" did not get me where I wanted and I needed to Substitute the User Model, which I'm sure you've seen/read multiple times, but possibly not absorbed it (at least I know I didn't).
Once you get the hang of it, there's really not that much code and it's not too complicated either.
# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
'''
Here is your User class which is fully customizable and
based off of the AbstractUser from auth.models
'''
my_custom_field = models.CharField(max_length=20)
def my_custom_model_method(self):
# do stuff
return True
There are a couple things to look out for after this, some of which came up in django 1.7.
First of all, if you want the admin page to look like it did before, you have to use the UserAdmin
# admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
# Register your models here.
admin.site.register(get_user_model(), UserAdmin)
The other thing is that if you're wanting to import the User class in a models file, you have to import it from the settings and not with get_user_model(). If you run into this, it's easy to fix, so I just wanted to give you a heads up.
You can check out my seed project I use to start projects to get a full but simple project that uses a Custom User Model. The User stuff is in the main app.
From there all the Registration and Login stuff works the same way as with a normal Django User, so I won't go into detail on that topic. I hope this helps you as much as it has helped me!
I try to avoid to extend the user model as explained in the django docs.
I use this:
class UserExtension(models.Model):
user=models.OneToOneField(User, primary_key=True)
... your extra model fields come here
Docs of OneToOneField: https://docs.djangoproject.com/en/1.7/topics/db/examples/one_to_one/
I see these benefits:
the same pattern works for other models (e.g. Group)
If you have N apps, every app can extend the model on his own.
Creating the UserExtension should be possible without giving parameters. All fields must have sane defaults.
Then you can create a signal handler which creates UserExtension instances if a user gets created.
I prefer extend the User model. For example:
class UserProfile(User):
def __unicode__(self):
return self.last_name + self.first_name
department = models.CharField(max_length=100)
class SignupForm(forms.Form):
username = forms.CharField(max_length=30)
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
department = forms.CharField(label="Department", max_length=100)
To save the data
form = UserRegistrationForm(request.POST)
if form.is_valid():
client = UserProfile()
client.username = username
client.set_password(password)
client.first_name = first_name
client.department = department
client.save()
check how are you saving the data after validate the form

Django Admin Interface: Extend the 'groups' page show its users and allow adding new users

In the default admin interface, the group page is not very comprehensive. (attached a picture) I can't see the users that are part of that group, and I can't add any users from there (I have to go to the user's profile, and add them to the group one by one)
I tried to extend this functionality this from admin.py, but not sure how to do it. If auth_group, auth_user and auth_user_groups were user defined models in models.py, I'd probably do something like this:
from django.contrib import admin
from myproject.models import Group, User, GroupUserMembership
class MembershipInline(admin.TabularInline):
model = GroupUserMembership
extra = 1
class UserAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
class GroupAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
admin.site.register(Group, GroupAdmin)
admin.site.register(User, UserAdmin)
But this would result in
Exception Value: The model Group is already registered
(also I don't know how can I import auth_user_groups)
TL;DR How do I extend the Django Admin Interface to display the many-to-many relationship between the default auth models User and Groups? (like Permissions in the picture below)
You must first unregister the default User(model name) model admin before registering your own.
Try this
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Django AbstractUser not working properly

I am trying to inherit from AbstractUSer my models.py looks like:
class MyUser(AbstractUser):
created = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username',]
MyUser._meta.get_field_by_name('email')[0]._unique=True
now by declaring email as unique field and username as a required field my superuser is being created successfully and also is being authenticated properly but I am having a problem while creating any other user as if I am creating any user through my admin page its not being authenticated.It always returns
None
My admin.py:
from django.contrib import admin
from credilet.models import *
admin.site.register(MyUser)
What I am thinking is that the create_user is not being called properly as if I see in my admin page the password is not hashed so that means the create_user is not being called properly.Somebody please help through it or even if you have a proper documentation on abstractuser
not abstractbaseuser
so please refer that to me in the solutions.
Thanks
If you want to change the authentication system you have to use AbstractBaseUser,
look at this full example.
AbstractUser is ok to Extend Django’s default User.
I think you should add UserAdmin into admin.py for Myuser
if you does not add UserAdmin , password can't be hashed with django:
from django.contrib import admin
from credilet.models import *
from django.contrib.auth.admin import UserAdmin
admin.site.register(MyUser, UserAdmin) # add UserAdmin

Left Join in Django Model Multi-table Inheritance in Admin app

Scenario:
Let's say I want to extend Django's User model using multi-table inheritance. Let's say the model I created for that is called CustomUser.
Now let's assume that there are already existing records in the database corresponding to the User model and the table corresponding to the CustomUser model is still empty.
Now I want CustomUser model to be accessible from the Django's Admin app. What I noticed is only User model records which has a corresponding record in the CustomUser table is included in the change list of CustomUser, as if an INNER JOIN is being done behind the scene in the query... (I checked using connection.queries and it was indeed an INNER JOIN).
Now I want to change this behaviour so that a LEFT JOIN is done to retrieve records pertaining to CustomerUser.
How can I achieve this in Django?
Thank you very much!
Put below in any working admin.py.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
#### below imports your custom user model
from accounts.models import CustomUser
admin.site.unregister(User)
class UserProfileInline(admin.StackedInline):
model = CustomUser
class UserProfileAdmin(UserAdmin):
inlines = [ UserProfileInline, ]
admin.site.register(User, UserProfileAdmin)
Above will show your CustomUser model in User in admin. now you can access User fields from UserProfileAdmin by User__first_name etc. pardon for bad english.

add an image field to admin.auth admin site in django

Hi ive not be able to find any one trying to solve this issue yet so i thought id ask the question here. Basically im trying to add an image field to the admin.auth users admin site that relates to my userprofile model.
Aswell as users that register to the site, the admins need to be able to specify a profile picture when the create a user in the admin site that will be used for posts they make to news and blogs etc.
Is this at all possible, i have the userprofile model working which will allow me to add the image but i want to include this in the admin user panel when an admin creates a new user.
Any help is greatly appreciated!!
Thanks in advance
You can add your UserProfile (with the extra image field) as an inline to the User adminModel. So when you go to the admin.auth panel to add a new user, there will be an inline below it with all the UserProfile fields.
Something like this should do it (in admin.py of whatever app your userprofile is in):
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.contrib import admin
class UserProfileInline(admin.StackedInline):
model = UserProfile
class UserModelAdmin(UserAdmin):
inlines = [UserProfileInline,]
admin.site.unregister(User)
admin.site.register(User,UserModelAdmin)