Django Interleaving UserProfile with Profile in Admin - django

I have a User Profile which is currently shown in the Admin via a Stacked Inline. However because I have fields such as last_name_prefix and last_name_suffix (for foreign names such as Piet van Dijk to cover proper sorting by last name) I would like to be able interleave the user profile fields with the normal change user fields. So in the Change User admin interface it would appear like this:
First Name:
Last Name Prefix:
Last Name
Last Name Suffix:
I have tried this solution: http://groups.google.com/group/django-users/browse_thread/thread/bf7f2a0576e4afd1/5e3c1e98c0c2a5b1. But that just created extra fields in the user form that weren't actually coming from the user profile (they stayed empty even though they should get values from the user profile).
Could someone explain to me if this could be done and how?
Thanks very much!

I'm pretty sure you'd need to overwrite normal User admin.
What I would actually do is create a special forms.ModelForm for UserProfile called, say UserProfileAdminForm which included fields from the User model as well. Then you'd register UserProfile for admin and the save function for the UserProfileAdminForm would capture the user-specific fields and either create or update the User record (This is left as an exercise to the OP).
More info
When I say add more fields to a form, I mean manually add them:
class UserProfileAdminForm(forms.ModelForm):
username = forms.CharField(...)
email = forms.EmailField(...)
first_name = ...
last_name = ...
def __init__(self, *args, **kwargs):
super(UserProfileAdminForm, self).__init__(*args, **kwargs)
profile = kwargs.get('instance', None)
if profile and profile.user:
self.user = profile.user
self.fields['username'].initial = self.user.username
self.fields['last_name'].initial = ...
...
class Meta:
model = UserProfile

This question has been solved by the new Django version 1.5: https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#auth-custom-user.

Related

Is there a way to have a field that only the superuser can add/edit?

Is there a way to have a field that only the superuser can add/edit?
class ProductPage(Page):
price = models.IntegerField(blank=True)
description = RichTextField(blank=True)
featured = models.BooleanField(default=False)
Above is part of my model but i only want the superuser to access the featured field.
I'm assuming you mean that only superusers logged in the Django admin site should be able to edit the featured field. If you want to restrict access in your own forms and views, you just need to check the user's status and customize the form/view accordingly. Here's what you can do in admin.py, in your ModelAdmin:
def get_readonly_fields(self, request):
fields = super().get_readonly_fields(request)
if not request.user.is_superuser:
fields.append('featured')
return fields
I tried out the solution given by #dirkgroten but had to make a few changes to make it work.
According to the Django Documentation for >2.1 versions of Django, the get_readonly_fields take 3 parameters.
Also, fields need to set a list to append, as it creates a tuple on default.
def get_readonly_fields(self, request, obj=None):
fields = list(super().get_readonly_fields(request))
if not request.user.is_superuser:
fields.append('featured')
return fields

Profile User object displaying all user django

I have a CustomUser and a Profile app in my profile app i have following models:
class Profile(models.Model):
user = models.OneToOneField(MyUser,unique=True)
name = models.CharField(_('Name'),max_length=100)
work = models.CharField(_('Position'),max_length=200)
company = models.CharField(_('Company'),max_length=200)
gender = models.CharField(_('Gender'),max_length=10)
photo = models.ImageField(_('Profile Pic'),upload_to='images/',blank=True)
bio = models.TextField(_('Bio'),max_length=300)
def __str__(self):
return self.name
My main problem is when i include user field in forms it displays a drop down list of all user who are registered with the app.
And i want to show only loggedin user
I think this approach is better and saves a time:
user = MyUser.objects.filter(username__icontains=request.user)
form.fields['user'].queryset = user
This is a question specific to the form you are using, rather than the model. What you want to do is specify a queryset for the field user.
You may do this after instantiating your initial form. The following would go in your view, where you declare your form for get requests:
# Obtain set of authenticated users (see [1] below)
authenticated_users = []
users = User.objects.all()
for user in users:
if user.is_authenticated():
authenticated_users.append(user)
# Instantiate form and update `user` queryset
form = SomeForm()
form.fields['user'].queryset = authenticated_users
[1] I don't believe it's possible to filter the User object manager by whether a user is authenticated or not, unfortunately, so you must iterate through the queryset and check for each user. If anyone knows such a query, then I'd be happy to change my answer to it.
This will limit the dropdown to authenticated users.

Django Customizing authentication user results in 2 fields in admin

I've followed https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#extending-the-existing-user-model to add a ManyToMany field for what games a user has played.
class Profile(models.Model):
""" Extended authentication profile storing specific info """
user = models.OneToOneField(User)
owned = models.ManyToManyField(OwnedStruct)
then add to admin by the following
class ProfileInline(admin.StackedInline):
""" Show profile inline with user """
model = Profile
verbose_name_plural = 'profile'
class UserProfileAdmin(UserAdmin):
""" Add inline to User """
inlines = (ProfileInline,)
...
admin.site.unregister(User)
admin.site.register(User, UserProfileAdmin)
In the database things look fine, but in the admin I see two fields representing the ManyToMany OwnedStruct. Before messing with the user, it shows the first as "Profile #1" and the second as "Profile #2". After selecting some options from Profile 1's M2M and clicking save, it appears to update that field correctly. If I update Profile#2, it does not save or work or appear to change anything. I'd expect it to only show one. What could cause two Profiles?
If I understand correctly the problem is that for some reason django admin doesn't care about OneToOneField and create more than one inline forms for the Profile. You can try to fix that with adding max_num = 1 to your ProfileInline class.
It must look something like:
class ProfileInline(admin.StackedInline):
""" Show profile inline with user """
model = Profile
max_num = 1
verbose_name_plural = 'profile'

'UpdateView with a ModelForm as form_class'-issue

The code I would like to get is for a page that has a simple form of one field to change a user's email address using an UpdateView.
Sounds simple, but the difficulty is that I want the URL mapping url(r'email/(?P<pk>\d+)/$', EmailView.as_view(),) not to use the id of the Model used in my ModelForm (User) but the id of another Model (Profile).
The id of a Profile instance of a specific user can be called as follows inside a view: self.user.get_profile().id. I am using the Profile model of the reusable app userena if you are wondering.
A (afaik not optimally implemented ¹) feature of an UpdateView is that if you want to use your own ModelForm instead of letting the UpdateView derive a form from a Model you need to(otherwise produces an Error) define either model, queryset or get_queryset.
So for my EmailView case I did the following:
forms.py
class EmailModelForm(forms.ModelForm):
class Meta:
model = User
fields = (
"email",
)
def save(self, *args, **kwargs):
print self.instance
# returns <Profile: Billy Bob's Profile> instead of <User: Billy Bob> !!!
return super(EmailModelForm, self).save(*args, **kwargs)
views.py
class EmailView(UpdateView):
model = Profile # Note that this is not the Model used in EmailModelForm!
form_class = EmailModelForm
template_name = 'email.html'
success_url = '/succes/'
I then went to /email/2/. That is the email form of the user that has a profile with id 2.
If I would run a debugger inside EmailView I get this:
>>> self.user.id
1
>>> profile = self.user.get_profile()
>>> profile.id
2
So far so good. But when I submit the form it won't save. I could overwrite the save method in the EmailModelForm but I'd rather override something in my EmailView. How can I do that?
¹ Because UpdateView could just derive the model class from the ModelForm passed to the form_class attribute in case it is a ModelForm.
Having your view and model form correspond to different models seems a bad idea to me.
I would set model = User in your EmailView, then override get_object so that it returns the user corresponding to the given profile id.

Automatic author in Django admin

all. I'm working on the admin for my django site, and I've run into an obstacle.
I've got an Entry model and a Related model. The Related model has two foreign key fields: one to the Entry model (entry) and one to django's User model (author). The Related model is considered a "sub-model" of the Entry model, and each user can only have one Related per Entry.
In the admin, Related is edited inline with Entry. As I have it, the admin shows only one extra Related at a time, and it automatically fills the author field with the current user:
from django.contrib import models
from django.contrib.auth.models import User
class Entry(models.Model):
pass
class Related(models.Model):
entry = models.ForeignKey(Entry)
author = models.ForeignKey(User)
class Meta:
unique_together = ('entry', 'author')
from django.contrib import admin
class RelatedInline(admin.StackedInline):
model = Related
exclude = ('author',)
max_num = 1
class EntryAdmin(admin.ModelAdmin):
inlines = (RelatedInline,)
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in filter(lambda obj: isinstance(obj, Related), instances):
if instance.__dict__.get('author', None) is None:
instance.author = request.user
instance.save()
formset.save_m2m()
The problem is that if a user wants to edit an entry which already has a Related by anyone, then only that one related field will be shown.
If possible, I wonder if anyone has any ideas about how I could keep a setup similar to this, but have the admin automatically display the user's related if it exists and an empty form if it doesn't. Barring that, I would just get rid of the line max_num = 1 and replace it with extra = 1. Of course, this would mean that a "new related" form would show even if the user already had one for the current entry, so I wonder if anyone has any idea about how I would catch a possible IntegrityError and let the user know that an error had occurred.
It turns out this is pretty simple. You just need to add a queryset function to your RelatedInline class, specifying which inline to show. If the returned queryset has at least one member, the first will be shown. If the queryset is empty, a single blank inline will be shown!
class RelatedInline(admin.StackedInline):
model = Related
exclude = ('author',)
max_num = 1
def queryset(request):
return Related.objects.filter(author = request.user)