In my ModelForm, I have to override some settings of the fields (e.g. choices, or required state). This requires declaring the entire field again as formfield.
Is there a simple way to access the verbose_name of the model field, so this doesn't have to redefined?
You don't have to redefine the field to change these settings. You can access the field in the form __init__ like below.
class MyForm(forms.ModelForm):
class Meta(object):
model = MyModel
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['my_field'].required = True
Related
I have such a problem: I want to limit foreign key choices in inline model admin, but I want to do this based on some values specified in this inline object.
How can I access inline object (not parent object) inside InlineModelAdmin methods (specifically in formfield_for_foreignkey(self, db_field, request, **kwargs))?
You cannot access to an inline object inside any InlineModelAdmin methods.
The only way is to use a custom ModelForm which receive instance when is initialized. Pay attention that extra forms to create new inline object receive and "empty" instance (pk=None).
from django.forms import ModelForm
from django.contrib.admin import ModelAdmin, TabularInline
class QuestionForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
# do what you want with inline object instance
class Meta:
model = Question
fields = "__all__"
class QuestionInline(TabularInline):
model = Question
form = QuestionForm
class CategoryAdmin(ModelAdmin):
model = Category
inlines = [QuestionInline]
This should work for you. Change "YourModelAdmin", "target_field", "your_custom_condition" and '...' with your proper values.
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
field = super(YourModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
if db_field.name == 'target_field':
if request and your_custom_condition:
field.queryset = field.queryset.filter(...)
return field
I have a simple model form what I use through the admin interface. Some of my model fields store datas that require a bit more time to calculate (they come from other sites). So I decided to put an extra boolean field to the form to decide to crawl these datas again or not.
class MyModelForm(forms.ModelForm):
update_values = forms.BooleanField(required=False) #this field has no model field
class Meta:
model = MyModel
This extra field doesn't exist in the model because only the form needs it.
The problem is that I only want it to appear if it's an existing record in the database.
def __init__(self, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
if self.instance.pk is None:
#remove that field somehow
I tried nearly everything. Exclude it, delete the variable but nothing wants to work. I also tried dynamically add the field if self.instance.pk is exists but that didn't work too.
Any idea how to do the trick?
Thanks for your answers.
You could subclass the form and add the extra field in the subclass:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
class MyUpdateModelForm(MyModelForm):
update_values = forms.BooleanField(required=False) #this field has no model field
class Meta:
model = MyModel
You can then override the get_form method of your admin, which is passed the current instance: get_form(self, request, obj=None, **kwargs)
Rather than removing the field in __init__ if instance.pk is not None, how about adding it if it is None? Remove the class-level declaration and just change the logic:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk is not None:
self.fields['update_values'] = forms.BooleanField(required=False)
Is there any way to load different admin forms for editing an objects depending of what object is needed to be updated?
For example - we have an MPTTModelAdmin objects. And for root objects we don't want to see some fields:
class RootObjectForm(ModelForm):
class Meta:
model = Author
exclude = ('title',)
class ChildObjectForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'birth_date')
But I don't know how to get object fields in forms.py or admin.py.
You can always supply your own form class for a ModelAdmin class: https://docs.djangoproject.com/en/1.5/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form
From there you can access fields by key, just like any other Django form:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyModeForm, self).__init__(*args, **kwargs)
# access whatever field by key
# self.fields['field-name']
In forms.py file you can get object fields and their value at two stages.
1 : when form is submitted.
clean method does initial validations.
def clean(self):
""" validation of address form """
cleaned_data = super(WebsiteAddressForm, self).clean()
field1_value = self.cleaned_data.get("field1")
print field1_value
return cleaned_data
2 : when form is initialized. ____init____ method will call.
class MyForm(forms.ModelForm):
class Meta:
model = Model1
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
#self.fields['field1']
I use a ModelForm and I want to set a max_value for an IntegerField without losing the other attributes which where created from the model (verbose_name, etc.).
This is my ModelForm:
class DataForm(ModelForm):
def __init__(self, *args, **kwargs):
super(DataForm, self).__init__(*args, **kwargs)
self.fields['start_range_points'].max_value = 1000
class Meta():
model = DataModel
This doesn't work, django does not apply the validation for large numbers. If I create the field in the following way the validation works but I lose the information which was created from the Model.
class DataForm(ModelForm):
start_range_points = forms.IntegerField(min_value=0, max_value=1000)
class Meta():
model = DataModel
What can I do to achieve something similar to attempt #1?
The validator for max_value is added in IntegerField's __init__ function if max_value is present. So you will need to manually add the validator, something like:
from django.core.validators import MaxValueValidator
class DataForm(ModelForm):
def __init__(self, *args, **kwargs):
super(DataForm, self).__init__(*args, **kwargs)
validators = [ v for v in self.fields['start_range_points'].validators if not isinstance(v, MaxValueValidator) ]
validators.append( MaxValueValidator(1000) )
self.fields['start_range_points'].validators = validators
class Meta():
model = DataModel
I'm using the Django Form View and I want to enter custom choices per user to my Choicefield.
How can I do this?
Can I use maybe the get_initial function?
Can I overwrite the field?
When I want to change certain things about a form such as the label text, adding required fields or filtering a list of choices etc. I follow a pattern where I use a ModelForm and add a few utility methods to it which contain my overriding code (this helps keep __init__ tidy). These methods are then called from __init__ to override the defaults.
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('country', 'contact_phone', )
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
self.set_querysets()
self.set_labels()
self.set_required_values()
self.set_initial_values()
def set_querysets(self):
"""Filter ChoiceFields here."""
# only show active countries in the ‘country’ choices list
self.fields["country"].queryset = Country.objects.filter(active=True)
def set_labels(self):
"""Override field labels here."""
pass
def set_required_values(self):
"""Make specific fields mandatory here."""
pass
def set_initial_values(self):
"""Set initial field values here."""
pass
If the ChoiceField is the only thing you're going to be customising, this is all you need:
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('country', 'contact_phone', )
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
# only show active countries in the ‘country’ choices list
self.fields["country"].queryset = Country.objects.filter(active=True)
You can then make your FormView use this form with like this:
class ProfileFormView(FormView):
template_name = "profile.html"
form_class = ProfileForm