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)
Related
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'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
I have a modelform and im creating additional fields (that do not exist in model) for its form.
I know you can reorder the fields in modelform like it says in the docs.
But the problem is - i want the additional fields to be rendered BEFORE the other fields.
Is it possible to somehow reorder the fields of the form before rendering? How does form object keep track of the order of its fields anyway?
Alan
No matter. It seems i found answer already and this seems to do the trick, since i have added 2 additional fields:
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.build_fields()
l = len(self.fields.keyOrder)
for i in range(0,2):
f = self.fields.keyOrder[l-1]
self.fields.keyOrder.pop(l-1)
self.fields.keyOrder.insert(0, f)
This above was my initial fix. Later on i found out that it did not cut any more. Then i did this :
class AlertForm(forms.ModelForm):
class Meta:
model = Message
fields = model_fields
def __init__(self, *args, **kwargs):
super(AlertForm, self).__init__(*args, **kwargs)
self.build_fields()
newKeyOrder = []
newKeyOrder.append('field_that_had_to_be_first')
if typechange:
newKeyOrder.append('field_thats_sometimes_necessary')
newKeyOrder += model_fields
self.fields.keyOrder = newKeyOrder
The solutions above no longer works with django 2 (I don't know since when)...
But now, there's an ordered dict fields property on ModelForm that we can use to reorder the fields...
class MyForm(forms.ModelForm):
class Meta:
fields = ['model_field1', 'model_field2']
model = MyModel
extra_field = forms.CharField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for k in self._meta.fields:
self.fields.move_to_end(k)
The fields attribute of your ModelForm`s Meta class define which fields to show and in which order.
Use the fields attribute of the ModelForm's inner Meta class. This attribute, if given, should be a list of field names to include in the form. The order in which the fields names are specified in that list is respected when the form renders them.
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
I have a model which is accessible through the Django admin area, something like the following:
# model
class Foo(models.Model):
field_a = models.CharField(max_length=100)
field_b = models.CharField(max_length=100)
# admin.py
class FooAdmin(admin.ModelAdmin):
pass
Let's say that I want to show field_a and field_b if the user is adding an object, but only field_a if the user is editing an object. Is there a simple way to do this, perhaps using the fields attribute?
If if comes to it, I could hack a JavaScript solution, but it doesn't feel right to do that at all!
You can create a custom ModelForm for the admin to drop the field in the __init__
class FooForm(forms.ModelForm):
class Meta(object):
model = Foo
def __init__(self, *args, **kwargs):
super(FooForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
# Since the pk is set this is not a new instance
del self.fields['field_b']
class FooAdmin(admin.ModelAdmin):
form = FooForm
EDIT: Taking a hint from John's comment about making the field read-only, you could make this a hidden field and override the clean to ensure the value doesn't change.
class FooForm(forms.ModelForm):
class Meta(object):
model = Foo
def __init__(self, *args, **kwargs):
super(FooForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
# Since the pk is set this is not a new instance
self.fields['field_b'].widget = forms.HiddenInput()
def clean_field_b(self):
if self.instance and self.instance.pk:
return self.instance.field_b
else:
return self.cleaned_data['field_b']
You can also do the following
class FooAdmin(admin.ModelAdmin)
def change_view(self, request, object_id, extra_context=None):
self.exclude = ('field_b', )
return super(SubSectionAdmin, self).change_view(request, object_id, extra_context)
Taken from here Django admin: exclude field on change form only