Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Show filter_horizontal on 'User Change' admin page

I have the following model:

class Hospital(models.Model):
    name = models.CharField(max_length=200)
    authorized_users = models.ManyToManyField(User)

Displaying a filter_horizontal widget on the Hospital admin page to manage the ManyToManyField is pretty simple:

class HospitalAdmin(admin.ModelAdmin):
    filter_horizontal = ('authorized_users', )

admin.site.register(models.Hospital, HospitalAdmin)

However, what I'd REALLY like is to display that widget AS WELL on the "Change User" admin page, inline with the rest of the User's information. It stands to reason that ManyToManyFields SHOULD be modifiable from both directions - to authorize multiple users for a single hospital, the above de-facto situation is fine. However, to authorize one user on multiple hospitals, the current situation would require visiting the admin page for each hospital and selecting the one user in question - absurd.

I will add that I AM using the UserProfile methodology to store additional info about users (what type of user they are, etc). One POSSIBLE solution would be to make the Hospital's ManyToManyField reference UserProfile instead of User. Then I could add a ManyToManyField(Hospital, through=Hospital.authorized_users.through) to UserProfile, thus allowing me to add the filter_horizontal widgets to both ends. However, this is non-ideal since it'd be a pain later to reference the connection. Imagine I want to get the first user authorized for a given hospital. Rather than hosp_name.authorized_users.all()[0], I'd have to do something like hosp_name.authorized_users.all()[0].user. I'm not even sure how I'd accomplish the equivalent of hosp_name.authorized_users.all() to get the full list (as that would return a list of UserProfiles, not Users.

like image 593
Alex Whittemore Avatar asked Oct 20 '25 09:10

Alex Whittemore


1 Answers

More eloquently stated, my goal was to make the administration of the many-to-many hospital-user relationship bidirectional. Which is to say, on the admin page for Hospitals, you'd get a filter_horizontal for users, and on the User admin page you'd get a filter_horizontal for Hospitals.

I ditched the idea of making the relationship with User, and instead made it with UserProfile. I ended up using the exact code mentioned at the bottom of this 7 year old standing Django ticket.

in the end, my code is

#models.py
class ReverseManyToManyField(models.ManyToManyField):
    pass
try:
    import south
except ImportError:
    pass
else:
    from south.modelsinspector import add_ignored_fields
    add_ignored_fields([".*\.ReverseManyToManyField$",])

class Hospital(models.Model):
    name = models.CharField(max_length=200)
    authorized_users = models.ManyToManyField('UserProfile', blank=True)
    def __unicode__(self):
        return self.name

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    authorized_hospitals = ReverseManyToManyField(Hospital, through=Hospital.authorized_rads.through)

    def __unicode__(self):
        return self.user.username

and

#admin.py
##### Stuff to register UserProfile fields in the admin site
class UserProfileInline(admin.StackedInline):
    model=models.UserProfile
    can_delete = False
    verbose_name_plural = 'profiles'
    filter_horizontal = ('authorized_hospitals',)

class UserAdmin(UserAdmin):
    inlines = (UserProfileInline, )

class HospitalAdmin(admin.ModelAdmin):
    filter_horizontal = ('authorized_users', )

admin.site.unregister(User)
admin.site.register(User, UserAdmin)
##### END UserProfile Stuff

admin.site.register(models.Hospital, HospitalAdmin)
like image 139
Alex Whittemore Avatar answered Oct 21 '25 23:10

Alex Whittemore



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!