I have created a custom widget which has a link to some ajax functionality. In order to get this to work, I need to pass the pk of the instance I am currently editing. It seems that widgets have no way of accessing the current model instance (for good reason!) so I was wondering how I would get this infor开发者_如何学编程mation? Would I have to obtain it from the uri or is there a handy method I am overlooking which will give me what I need.
Thanks
You can override the __init__
method of the widget and pass it the the form instance. From the form instance you can get the pk if exists.
In this answer, they bind the form_instance to the widget on the init of the form.
django - how can I access the form field from inside a custom widget
Here is another question post talking about custom widgets accessing form instances.
Country/State/City dropdown menus inside the Django admin inline
In addition to chosen answer: To use AdvancedModelChoiceField, and still render form via regular widgets, i subclassed django widget and overrided some methods. first method be optgroups. Replace
for index, (option_value, option_label) in enumerate(chain(self.choices)):
with
for index, (option_value, option_label, obj) in enumerate(chain(self.choices)):
Then, we need to pass that obj to option.
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None, obj=None):
option = super(RadioSelect, self).create_option(name, value, label, selected, index, subindex, attrs)
option['obj'] = obj
return option
In widget template access obj like this:
{{ widget.obj.object_attr }}
And finally, we can use our custom widget and field in our form:
class MyForm(forms.ModelForm):
myfield = AdvancedModelChoiceField(
widget=MyWidget,
queryset=MyModel.objects.all(),
empty_label=None
)
Override the model field that you will use this widget with, and set your widget as default widget. When setting the default widget, instantiate the widget and pass whatever you need.
For example, I override the ForeignKey class to add a link after the select input.
The widget:
class SelectAdd(widgets.Select):
def __init__(self, related_name, related_create_url, attrs=None):
self.related_name = related_name
self.related_create_url = related_create_url
super().__init__(attrs=attrs)
def render(self, name, value, attrs=None, **kwargs):
rendered = super(SelectAdd, self).render(name, value, attrs, **kwargs)
return rendered + mark_safe(u'''
<span>Add new {} : {}</span>
'''.format(self.related_name, self.related_create_url))
And the model field:
class ForeignKeyAdd(ForeignKey):
def formfield(self, **kwargs):
db = kwargs.pop('using', None)
if isinstance(self.remote_field.model, six.string_types):
raise ValueError("Cannot create form field for %r yet, because "
"its related model %r has not been loaded yet" %
(self.name, self.remote_field.model))
defaults = {
'form_class': forms.ModelChoiceField,
'queryset': self.remote_field.model._default_manager.using(db),
'to_field_name': self.remote_field.field_name,
'widget': SelectAdd(
related_name=self.name,
related_create_url=get_crud_url(self.related_model, 'create')
),
}
defaults.update(kwargs)
return super(ForeignKey, self).formfield(**defaults)
精彩评论