Custom list_editable field in django admin change list, which doesn't correspond directly to a model field

匿名 (未验证) 提交于 2019-12-03 02:56:01

问题:

Assuming my model looks like this (this is a simplified example):

class Person(Model):   first_name = CharField(...)   last_name = CharField(...)    def name():     return first_name + ' ' + last_name

Displaying the name as a single column in the admin change list is easy enough. However, I need a single, editable "name" field that is editable from the list page, which I can then parse to extract and set the model field values. The parsing isn't a concern. I am just wondering how to have an editable form field on the list page that doesn't correspond directly to a model field.

回答1:

You should be able to do this in pure Python with a bit of work. Basically, you need to use the get_changelist_form method on the admin class to tell it to use a custom form rather than a default ModelForm for your instances, then initialize the custom field's value properly (most conveniently in the form's __init__ method) and specialize the save behavior of that form to set the first_name and last_name values.

Something like this should be a start:

class PersonChangeListForm(forms.ModelForm):     class Meta:         model = Person     name = forms.CharField()      def __init__(self, *args, **kwargs):         instance = kwargs.get('instance')         if instance:             initial = kwargs.get('initial', {})             initial['name'] = '%s %s' % (instance.first_name, instance.last_name)             kwargs['initial'] = initial         super(PersonChangeListForm, self).__init__(*args, **kwargs)      def save(self, *args, **kwargs):         # use whatever parsing you like here         first_name, last_name = self.cleaned_data['name'].split(None, 1)         self.cleaned_data['first_name'] = first_name         self.cleaned_data['last_name'] = last_name         super(PersonChangeListForm, self).save(*args, **kwargs)  class PersonAdmin(admin.ModelAdmin):     def get_changelist_form(self, request, **kwargs):         return PersonChangeListForm

You will also need to declare a list_editable value that evaluates to True when tested as a boolean - some of the admin processing short-circuits without using the formset if list_editable does not evaluate as True.

If you have no other fields you want to be editable, this gets more complicated. The class validation requires that everything in the list_editable sequence be an editable field that's declared in list_display as well and is not a display link field. I think the options there are either to override the admin class's changelist_view method to use the full processing even if list_editable is not true, or to define a custom subclass of list or tuple that evaluates to True even when empty so it can pass validation. The former would require repeating a great deal of standard code and significantly increase your maintenance burden if you ever upgrade, while the latter is a counterintuitive hack and would not at all surprise me if it had unexpected consequences.

Neither are good options, so I hope you have at least one other field that makes sense to include in list_editable.



回答2:

I just tried a quick mock-up of the problem in the admin. It seems that the admin validation fails for a field which is in list_editable that is not defined on the model. In short, the answer to your question seems to be no.

However, that doesn't mean it's not doable. With a bit of Javascript, you could use X-editable

(or roll your own), and make the "Name" column editable. Create a view to validate the data and save it to the model. Set X-editable field 'url' parameter to post to this URL. Obviously decorate your view with login_required / permissions_required etc, to make sure no-one else can edit the data.



回答3:

Addition to @Peter DeGlopper answer: you may add other model field in list_editable (for example last_name), then override change_list.html (look this answer How to override templates and add to the end of file some javascrip code: templates/admin/your_app/person/change_list.html:

{% extends "admin/base_site.html" %} {% load i18n admin_urls static admin_list %} ...  {% block content %} ... {% endblock %}  {% block footer %} {{ block.super }} <script>     var last_names = document.querySelectorAll('.field-last_name');      last_names.forEach(function(el) {         var input = el.children[0];         var text = input.value;         input.hidden = true;         input.style.display = 'none';         el.append(text);     }); </script> {% endblock %}


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!