开发者

Django - Get instance pk inside form widget class

开发者 https://www.devze.com 2023-02-21 00:16 出处:网络
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 wa

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)
0

精彩评论

暂无评论...
验证码 换一张
取 消