Can a ManyToManyField inside a ModelForm use a forms.MultipleHiddenInput widget instead of the default forms.MultipleChoiceField? In the example below 'groups' is a ManyToManyField on the Model 'Test':
class TestSelectionForm(forms.ModelForm):
class Meta:
model = Test
fields = ('groups')
widgets = {
'groups': forms.MultipleHiddenInput()
}
def __init__(self, *args, **kwargs):
super(TestSelectionForm, self).__init__(*args, **kwargs)
self.fields['groups'].queryset = Group.objects.filter(...)
But no hidden input fields were rendered for this form. Am I missing something or is it just not possible to use MultipleHiddenInput together with a ManyToManyField? (Should I then just write the HTML for the hidden inputs inside the template or is there a different approach I could use from within the ModelForm)?
Related
I would like the visible fields of a ModelForm to be determined by the instance of the model class it is built upon.
The ModelForm is built upon the model protocol which has a method (protocol.visiblefields())which returns an array of strings corresponding to the fields to be made visible.
forms.py
class ListForm(ModelForm):
class Meta():
model = protocol
fields = []
views.py
newProtocol = protocol()
form = ListForm(instance=newProtocol)
You have a couple of options to do this, one is mostly in the view and the other is in the form __init__.
Option 1: Pop the fields after initializing the form. Although you would need to specify all fields in the form Meta and remove the ones you don't want in the view.
newProtocol = protocol()
form = ListForm(instance=newProtocol)
visible_fields = newProtocol.visiblefields()
for field in form.fields:
if field not in visible_fields:
form.fields.pop(field)
Option 2: Overwrite the __init__ method and pass the fields in. We include all fields and remove the ones we don't want.
forms.py
class ListForm(ModelForm):
class Meta:
model = protocol
fields = '__all__'
def __init__(self, *args, **kwargs):
visible_fields = kwargs.pop('visible_fields')
super(ListForm, self).__init__(*args, **kwargs)
for field in self.fields:
if field not in visible_fields:
self.fields.pop(field)
views.py
newProtocol = protocol()
form = ListForm(instance=newProtocol, visible_fields=newProtocol.visiblefields())
I have a model in my project which is rendered as an inline of an other model.
The inline model has a property (not a field) that I need in my template for a particular check, but I don't want it shown as a field. I can't find a way to make such field rendered as hidden, so that the entire <TD> disappears.
I've both tried with Meta class:
class MyFormset(forms.models.BaseInlineFormSet):
class Meta:
widgets = {
'to_be_hidden_property': forms.HiddenField,
}
and with add_fields method:
class MyFormset(forms.models.BaseInlineFormSet):
def add_fields(self, form, index):
super(MyFormset, self).add_fields(form, index)
form.fields["to_be_hidden_property"].widget = forms.HiddenInput()
but both failed. First attempt simply gets ignored, while second one leads to a KeyError exception (field doesn't exist).
I'm open to suggestions, as I'd really hate to have to hide it via JS.
EDIT (adding admin.TabularInline code):
class AssegnamentoCommessaConsulenzaInline(admin.TabularInline):
formset = AssegnamentoCommessaConsulenzaFormset
model = AssegnamentoCommessaConsulenza
template = 'admin/commessa/edit_inline/assegnamentocommessaconsulenza_tabular.html'
extra = 0
readonly_fields = (
'get_fase',
'get_configuration_link_inline',
'numero_erogazioni_effettuate', # Field to hide
)
class Media:
js = ('useless things', )
css = {'all': ('even less useful things', )}
EDIT (to explain what I've tried after #Alasdair's suggestion):
from django.forms.models import inlineformset_factory
class MyFormAbstract(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyFormAbstract, self).__init__(*args, **kwargs)
self.fields['to_be_hidden_property'] = forms.CharField(
widget=forms.HiddenInput(),
initial=self.instance.to_be_hidden_property
)
class Meta:
model = MyModel
fields = '__all__'
MyFormset = inlineformset_factory(
MyMainModel,
MyInlineModel,
form=MyFormAbstract
)
This whole thing seems to get silently ignored. There are no errors, but the property is still there.
Instead of subclassing BaseInlineFormSet, you might find it easier to customize the model form.
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['property_field'] = forms.CharField(widget=forms.HiddenInput(), initial=self.instance.property_field)
MyInlineFormset = inlineformset_factory(MyMainModel, MyModel, form=MyForm)
Note that all this does is add the hidden field to the form, you will have to add any additional processing you want.
I'm using a modelformset_factory to edit multiple instances of Product in the same form:
ProductFormSet = modelformset_factory(Product, fields=('code', 'state'))
form_products = ProductFormSet()
It works well.
But now I need to display an additional field of the Product model in the form but only for a specific instance of Product. I'm not sure if it can be done in a simple manner in Django. Is it possible to do so using a modelformset_factory?
You can specify the form in the modelformset_factory, so create a model form (in forms.py if you have one) override the __init__method to add extra fields.
I would move the fields from the formsetfactory arguments to the form
in forms.py (assuming you have one)
class ProductForm(forms.ModelForm):
model = Product
def __init__(self, *args, **kwargs):
super(ProductForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs :
product = kwargs['instance']
# to add an extra field, add something like this
self.fields['extra_field'] = forms.CharField(max_length=30)
class Meta:
fields = ('code', 'state')
Then pass that to your modelformset factory with the form argument
ProductFormSet = modelformset_factory(Product, form=ProductForm )
form_products = ProductFormSet()
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.