I tried to use django.forms.ModelMultipleChoiceField in one of the model's admin page in order to update m2m relation through form. I also specified django.contrib.admin.widgets.FilteredSelectMultiple as a widget for form's field and achieved my purpose successfully. The problem is, since I have over 10K related objects, all of them are included as an option in form page and that makes page very severely slow. For illustrating this problem I am adding here analogy of this case's appliance on django.contrib.auth.models.Group model and related 'users'.
admin.py
from django.contrib import admin
from django.contrib.auth.models import Group
from forms import GroupAdminForm
admin.site.unregister(Group) # get rid of default one
class GroupAdmin(admin.ModelAdmin):
form = GroupAdminForm
filter_horizontal = ['permissions']
admin.site.register(Group, GroupAdmin)
forms.py
from django import forms
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth.models import Group
User = get_user_model()
class GroupAdminForm(forms.ModelForm):
class Meta:
model = Group
exclude = []
users = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
required=False,
widget=FilteredSelectMultiple('users', False)
)
def __init__(self, *args, **kwargs):
super(GroupAdminForm, self).__init__(*args, **kwargs)
if self.instance.pk:
self.fields['users'].initial = self.instance.user_set.all()
def save_m2m(self):
self.instance.user_set.set(self.cleaned_data['users'])
def save(self, *args, **kwargs):
instance = super(GroupAdminForm, self).save()
self.save_m2m()
return instance
In the codes above, default admin change page for Group is disabled and implemented again that also includes input for group users. This is how it looks after I added 1000 test user objects:
As you see, all of 1000 users are included as an option. My question is that, how can I achieve to disable displaying them initially and display as the user types something in filter box?
Note: Please don't suggest me to use admin.ModelAdmin.raw_id_fields, since that is not user-friendly. Also, it would be better to solve this issue without help of any third-party package.
Related
I've added a plan field to my custom Account class, but cannot get it to show on the individual account page in the django admin. It shows correctly in table of all accounts in the list view (as denoted by list_display), but does not show on each individual account page.
Here's my Account model:
class Account(AbstractUser):
PLANS = (
("free", "free"),
("pro", "pro")
)
plan = models.CharField(max_length=10, choices=PLANS, default="free")
def __str__(self) -> str:
return self.first_name
And my admin file:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from accounts.models import Account
from subscribers.models import Subscriber
class SubscriberInline(admin.TabularInline):
model = Subscriber
extra = 0
class AccountAdmin(UserAdmin):
inlines = [SubscriberInline, ]
list_display = ("first_name", "plan", "email")
# fields = ("first_name", "plan", "email")
admin.site.register(Account, AccountAdmin)
Why does this happen?
Is the problem related to the custom Account class which inherits from the AbstractUser model? I thought to add fields to the AccountAdmin class, but that just returns the below error:
ERRORS:
<class 'accounts.admin.AccountAdmin'>: (admin.E005) Both 'fieldsets' and 'fields' are specified.
The plan field also doesn't show in the admin panel when trying to create a new account (mind you, neither do most of the other fields as it only asks for the username, password1 and password2 fields, and the option to add new subscribers to the table, but other fields like first_name etc can be edited in the admin after creation).
Thanks
UPDATE:
Adding user #bdbd's suggested changes seems to not make a difference to the admin area - am I adding this incorrectly? Here's my admin.py after adding #bdbd's changes:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from accounts.models import Account
from subscribers.models import Subscriber
from django.contrib.auth.forms import UserChangeForm
from django import forms
PLANS = (
("free", "free"),
("pro", "pro")
)
class MyAccountChangeForm(UserChangeForm):
plan = forms.ChoiceField(choices=PLANS)
class SubscriberInline(admin.TabularInline):
model = Subscriber
extra = 0
class AccountAdmin(UserAdmin):
form = MyAccountChangeForm
inlines = [SubscriberInline, ]
list_display = ("first_name", "plan", "email")
# #todo: bug! plan field not showing in django admin on individual account pages
admin.site.register(Account, AccountAdmin)
UPDATE 2:
Screenshots of admin area:
You should not extend from UserAdmin. Instead, you should create your own model admin class which extends from admin.ModelAdmin.
Then you should register your model separately.
class AccountAdmin(admin.ModelAdmin):
...
admin.site.register(Account, AccountAdmin)
As necessary, you can customize AccountAdmin to get the effect you want. You can peek at the UserAdmin source code to see how it is customized, if you want your admin view to behave similarly.
You need to override the default form that is being used by UserAdmin and add your field like so:
from django.contrib.auth.forms import UserChangeForm
from django import forms
class MyAccountChangeForm(UserChangeForm):
plan = forms.ChoiceField(choices=PLANS)
Then assign the form to your admin:
class AccountAdmin(UserAdmin):
form = MyAccountChangeForm
I create and save user from django-rest-framework, but it do when i am anonymous user. Now i dont see those model in django-admin. How to add model to django-admin?
admin.py
from django.contrib import admin
from possible_blacklist.models import PossibleBlacklist
class PossibleBlacklistAdmin(admin.ModelAdmin):
list_display = [field.name for field in PossibleBlacklist._meta.get_fields()]
admin.site.register(PossibleBlacklist, PossibleBlacklistAdmin)
serializers.py
from rest_framework import serializers
from possible_blacklist.models import PossibleBlacklist
class PossibleBlacklistSerializer(serializers.ModelSerializer):
class Meta:
model = PossibleBlacklist
fields = '__all__'
def create(self, validated_data):
return PossibleBlacklist.objects.create(**validated_data)
def validate_mobile_phone(self, data):
if data.startswith('0'):
raise serializers.ValidationError("Номер должен начинаться на +380")
return data
I had this problem and when I research, I've found this good documentation which will add the model automatically to your Django admin interface.
and no need to touch your admin with every single model which are you adding.
https://medium.com/hackernoon/automatically-register-all-models-in-django-admin-django-tips-481382cf75e5
I am trying to create custom fields for users to enter on signup with django-allauth. I have referred to several posts about this, but I am not able to get my custom form to save to my database. I do get a combined form on my signup.html page with username, password1 and 2, email and my extra fields of city and school, but I am not able to save the extra fields to the database. I have run syncdb and can see my User Profile table in the admin area.
This advice is the closest I have come to the answer but I do not understand how to implement it: "You can't use UserProfileForm to the allauth.SIGNUP_FORM_CLASS. You need to extend it from SignUpForm and write a save method which will accept the newly created user as the only parameter," from this post:
Custom registration form for use with django-allauth
I have also attempted to integrate advice on this form these posts:
Django Allauth not saving custom form
How to customize user profile when using django-allauth
This is my code:
Models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
# A required line - links a UserProfile to User.
user = models.OneToOneField(User)
# The additional attributes we wish to include.
school = models.CharField(max_length=128)
city = models.CharField(max_length=128)
def __unicode__(self):
return self.user.username
Forms.py
from django import forms
from django.contrib.auth.models import User
from myapp.models import UserProfile
from django.forms.widgets import HiddenInput
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('city', 'school')
def signup(self, request, user):
user=User.objects.get(email=request.email)
city=request.POST.get('city','')
school=request.POST.get('school','')
userprofile_obj = UserProfile(user=user,city=city,school=school)
userprofile_obj.save()
Settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'myapp.forms.UserProfileForm'
My template is the basic Signup.html from the django-allauth templates and I do not have a view made for this, although I attempted to make one from the tangowithdjango user authentication section register view, and this gave similar behavior (not saving to the database).
Thanks,
Kelly
Not sure if this is still an active question/issue for the original poster: if so, and for anyone else who comes across this, a few things to correct to at least move in the right direction:
I don't see an __init__() method that calls the superclass? E.g.:
def __init__(self, *args, **kwargs):
super(SignupForm, self).__init__(*args, **kwargs)
use the user parameter to the signup method. It should be populated; don't reload it.
Ensure the two objects are linking correctly (I didn't use Django to build my profile table so YMMV but I set user.profile = Profile(...); then execute user.profile.save() at the end of my signup() method.
get the values to place into the profile from the form cleaned_data (e.g. self.cleaned_data['city'] not the POST.
Then start debugging: is your signup() method firing? What does it get? What happens when you execute the profile.save() method?
Good evening,
I am presently creating a site with Django and I extended the user with a user profile. I have a small problem though. Here is my situation:
I extended the user profile in order to add custom fields.
I added the model to the User Admin Model, so when I am adding a user, I can fill in directly the fields to create the profile.
Now, if I don't add ANYTHING in these new custom user fields, in the user add page, Django Admin won't throw me an error saying these fields are null (and they aren't suppose to be)
I want it to throw me an error in this User Add Admin page, so that the admins will HAVE to fill in a profile when adding a new user.
ALL the users will be added in the Admin Panel.
Is this possible? Thanks a lot!
in admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.contrib.auth.models import User
from accounts.models import UserProfile
class UserProfileInline(admin.TabularInline):
model = UserProfile
class UserAdmin(DjangoUserAdmin):
inlines = [ UserProfileInline,]
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
In model.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
employee_number = models.PositiveIntegerField(unique=True)
def __unicode__(self):
return 'Number'
By default, empty inline is permitted and thus no further check would be taken for an empty form. You need to override it manually:
class UserProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
if self.instance.pk is None:
self.empty_permitted = False # Here
class Meta:
model = UserProfile
class UserProfileInline(admin.TabularInline):
model = UserProfile
form = UserProfileForm
I'm trying to add custom fields to an InlineFormset using the following code, but the fields won't show up in the Django Admin. Is the InlineFormset too locked down to allow this? My print "ding" test fires as expected, I can print out the form.fields and see them all there, but the actual fields are never rendered in the admin.
admin.py
from django.contrib import admin
import models
from django.forms.models import BaseInlineFormSet
from django import forms
from forms import ProgressForm
from django.template.defaultfilters import slugify
class ProgressInlineFormset(BaseInlineFormSet):
def add_fields(self, form, index):
print "ding"
super(ProgressInlineFormset, self).add_fields(form, index)
for criterion in models.Criterion.objects.all():
form.fields[slugify(criterion.name)] = forms.IntegerField(label=criterion.name)
class ProgressInline(admin.TabularInline):
model = models.Progress
extra = 8
formset = ProgressInlineFormset
class ReportAdmin(admin.ModelAdmin):
list_display = ("name", "pdf_column",)
search_fields = ["name",]
inlines = (ProgressInline,)
admin.site.register(models.Report, ReportAdmin)
I did it another way:
forms.py:
from django import forms
class ItemAddForm(forms.ModelForm):
my_new_field = forms.IntegerField(initial=1, label='quantity')
class Meta:
model = Item
admin.py:
from django.contrib import admin
from forms import *
class ItemAddInline(admin.TabularInline):
form = ItemAddForm
fields = (..., 'my_new_field')
This works so far, I only need to override somehow the save method to handle this new field. See this: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#form . It says that by default Inlines use BaseModelForm, which is send to formset_factory. It doesn't work for me, tried to subclass BaseModelForm with errors (no attribute '_meta'). So I use ModelForm instead.
You can do it by another way (Dynamic forms):
admin.py
class ProgressInline(admin.TabularInline):
model = models.Progress
extra = 8
def get_formset(self, request, obj=None, **kwargs):
extra_fields = {'my_field': forms.CharField()}
kwargs['form'] = type('ProgressForm', (forms.ModelForm,), extra_fields)
return super(ProgressInline, self).get_formset(request, obj, **kwargs)
model = models.Progress
In the admin there will be only the fields defined in this Progress model. You have no fields/fieldsets option overwriting it.
If you want to add the new ones, there are two options:
In the model definition, add those new additional fields (make them optional!)
In the admin model (admin.TabularInline), add something something like:
fields = ('newfield1', 'newfield2', 'newfield3')
Take a look at fields, fieldsets.