Dynamic forms in django-admin - python

I want to make admin add-form dynamic. I want to add few formfields depending on setting in related object.
I have something like this:
class ClassifiedsAdminForm(forms.ModelForm):
def __init__(self,*args, **kwargs):
super(ClassifiedsAdminForm, self).__init__(*args, **kwargs)
self.fields['testujemy'] = forms.CharField(label = "test")
And in admin.py:
class ClassifiedAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
return ClassifiedsAdminForm
As you can see, I want to add "testujemy" CharField to admin add-form and change-form. However, this way doesnt work. Is there any way to add field in init? It is working in normal view.

I've managed to do it using type().
class ClassifiedAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
adminform = ClassifiedsAdminForm()
fields = adminform.getNewFields()
form = type('ClassifiedsAdminForm', (forms.ModelForm,), fields)
return form
Hope it will help someone.

Related

Automatically updating a model field when it is created

I would like to automatically update a model field when it is created. So this is my situation. I have a custom User model that my customer can use to login. After they login, they will go to the account/profile page, which has a link to a form. Currently, when the user submits the form, it creates an instance of the LevelTest model(which is something I just need for the website to work). Here is the view class for the form:
class LevelTestView(generic.CreateView):
template_name = "leads/leveltest.html"
form_class = LevelTestModelForm
def get_success_url(self):
return reverse("profile-page")
and here is the LevelTestModelForm:
class LevelTestModelForm(forms.ModelForm):
class Meta:
model = LevelTest
fields = (
'first_name',
'last_name',
'age',
'username',
)
What I want to fill in automatically is the username field. In fact, I wish it doesn't even show up on the form itself when the user types in. The username is a field in the User Model, so I just want the new LevelTest's username field filled in with the current user's username. Hence, I used a post_save signal like below(which doesn't work):
def post_leveltest_created_signal(sender, instance, created, **kwargs):
if created:
instance.objects.update(
username=instance.user.username,
description='Add Description',
phone_number=instance.user.cellphone,
email=instance.user.username,
)
post_save.connect(post_leveltest_created_signal, sender=LevelTest)
I hope you guys could help me tweek the post_save signal, so that when the user creates a LevelTest instance, the LevelTest's username field(as well as the phone_number and email) is filled in with the user model's information. Thanks a lot!
If I understand you correct, you don't need to use signals, you can save username easier:
Extend get_form_kwargs method in your CreateView, like that:
class LevelTestView:(generic.CreateView)
...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Extend __init__ and save method in your Form, like that:
class LevelTestModelForm(forms.ModelForm):
...
def __init__(self, user, *args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
...
def save(self, commit=True):
leveltest = super().save(commit=False)
# I think it would be better if you saved only 'user' instance
# like this - leveltest.user = self.user (of course if you have fk to user model)
leveltest.username = self.user.username
leveltest.phone_number=self.user.cellphone
leveltest.email=self.user.username
leveltest.save()
return leveltest
I think #KIN1991's answer is pretty awesome, but you can minimize/optimize the code even more by just overriding the form_valid method. Like this:
class LevelTestView:(generic.CreateView)
...
def form_valid(self, form, *args, **kwargs):
user = self.request.user
form.instance.username = user.username
form.instance.phone_number=user.cellphone,
form.instance.email=user.username
return super().form_valid(form, *args, **kwargs)

Exclude fields but still provide a default value

I want to be able to hide a field from a form in the Django admin (I'm using Django 1.7), but still supply a default value (which is bound to the request as it is request.user).
Here are the contents of my admin.py:
from django.contrib import admin
from .models import News
class NewsAdmin(admin.ModelAdmin):
list_display = ('title', 'category', 'pub_date', 'visible',)
list_filter = ('visible', 'enable_comments', 'category__topic', 'category__site', 'category',)
search_fields = ['title']
def get_form(self, request, obj=None, **kwargs):
if not request.user.is_superuser:
self.exclude = ('author',)
return super(NewsAdmin, self).get_form(request, obj, **kwargs)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'author':
kwargs['initial'] = request.user.id
return super(NewsAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
admin.site.register(News, NewsAdmin)
And here is what it does:
As a superuser the author field correctly displays, preselecting the current user
As any other staff member allowed to create a News, the author field is hidden, but when the form is submitted an exception is raised:
IntegrityError at /admin/news/news/add/
Column 'author_id' cannot be null
How can I hide the author field and still provide an author_id?
Instead of excluding the field, you can set it to read only so that it will still show to the user, but they cannot change it:
self.readonly_fields = ('author',)
I finally found how to achieve what I wanted to do with a combination of two methods I have overriden.
Here the get_form method, now showing the author field as read-only instead of excluding it (thanks to this answer). This is a change that is being made for editing purposes only (see obj is not None) to prevent an unwanted exception while editing someone else's news.
def get_form(self, request, obj=None, **kwargs):
if not request.user.is_superuser and obj is not None:
self.readonly_fields = ('author',)
return super(NewsAdmin, self).get_form(request, obj, **kwargs)
And here is the formfield_for_foreignkey method. It simply filters the QuerySet to allow only one user.
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'author':
kwargs['initial'] = request.user.id
kwargs['queryset'] = User.objects.filter(pk=request.user.id)
return super(NewsAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Get the django user in save while using django.form

I have a problem getting the user in django when I use django forms. My code looks something like this.
The view:
#login_required
def something(request):
item = ItemForm(request.POST)
item.save(user=request.user)
The form:
class ItemForm(ModelForm):
class Meta:
model = Item
fields = '__all__'
def save(self, *args, **kwargs):
user = kwargs['user']
super(ItemForm, self).save(user=user)
The model
class Item(models.Model):
field = models.CharField(max_length=100,)
field2 = models.CharField(max_length=100,)
def check_permissions(self, user):
return user.groups.filter(name='group').exists()
def save(self, *args, **kwargs):
if self.check_permissions(kwargs['user']):
super(Item, self).save()
My problem is that when I call the default save in ItemForm I get an error because the user param is unexpected. I need the user in the model to make the permission check but I dont know how to get it.
I finally solved the problem. The way I found was to save the form without the user but with the commit flag set to False and then calling the function save from the model with the user param.
The form save method now looks like this
def save(self, *args, **kwargs):
item = super(ItemForm, self).save(commit=False)
item.save(user=kwargs['user'])

Modify variable just in the Django admin but not in database

I want to modify a single value when I open an instance of a model in the Django admin.
Basically I want to display a value that is the opposite of the one stored in the database. So here is what I did in admin.py :
class MyModelAdminForm(forms.ModelForm):
import_file = forms.FileField(required=False)
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyModelAdminForm, self).__init__(*args, **kwargs)
print(self.instance.value)
self.instance.value = self.instance.get_reverted_value()
print(self.instance.value)
I works I can see it with the prints, but the admin still displays the original value that is stored in database.
Any idea ?
Thanks in advance
What about:
def __init__(self, *args, **kwargs):
super(MyModelAdminForm, self).__init__(*args, **kwargs)
self.fields['value'].value = self.instance.get_reverted_value()

django admin: show list of items filtered

Am working on a django admin interface and I have a model which has a foreign key. In that field, am getting a drop down menu when the admin pages are viewed. Is there a way to filter the drop down result only where is_active=1 for example?
Regards,
limit_choices_to is what you are after.
If you only want the limited selection in your ModelAdmin you should tweak your ModelForm accordingly.
Something like this should do it:
class YourAdminForm(forms.ModelForm):
class Meta:
model = YourModel
def __init__(self, *args, **kwargs):
super(YourAdminForm, self).__init__(*args, **kwargs)
qs = self.fields['your_fk_field'].queryset
self.fields['your_fk_field'].queryset = qs.filter(is_active=1)
According to the docs
class MyModelAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "car":
kwargs["queryset"] = Car.objects.filter(is_active=1)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
If you need access to current object check this How do I get the actual object id in a Django admin page (inside formfield_for_foreignkey)?

Categories