更好的ArrayField管理小部件?

时间:2015-07-15 09:05:58

标签: django django-admin

有没有办法让ArrayField的管理窗口小部件允许添加和删除对象?似乎默认情况下,它只显示一个文本字段,并使用逗号分隔作为其值。

除了不方便之外,AFAICT在数组的基本字段是Char / TextField的情况下,这并不允许在数组的任何文本中包含逗号的任何方式。

7 个答案:

答案 0 :(得分:15)

我不相信(original source),但更简单的选择是在模型上继承ArrayField并覆盖默认的管理窗口小部件。接下来是一个基本实现(在Django 1.9和1.10中测试):

<强> models.py

from django import forms
from django.db import models
from django.contrib.postgres.fields import ArrayField


class ChoiceArrayField(ArrayField):
    """
    A field that allows us to store an array of choices.
    Uses Django's Postgres ArrayField
    and a MultipleChoiceField for its formfield.
    """

    def formfield(self, **kwargs):
        defaults = {
            'form_class': forms.MultipleChoiceField,
            'choices': self.base_field.choices,
        }
        defaults.update(kwargs)
        # Skip our parent's formfield implementation completely as we don't
        # care for it.
        # pylint:disable=bad-super-call
        return super(ArrayField, self).formfield(**defaults)


FUNCTION_CHOICES = (
    ('0', 'Planning'),
    ('1', 'Operation'),
    ('2', 'Reporting'),
)

class FunctionModel(models.Model):
    name = models.CharField(max_length=128, unique=True)
    function = ChoiceArrayField(
        base_field=models.CharField(max_length=256, choices=FUNCTION_CHOICES),
        default=list)

答案 1 :(得分:8)

答案 2 :(得分:2)

这是一个已经被接受的解决方案的更好版本。使用“CheckboxSelectMultiple”使其在管理页面中更有用。

class ChoiceArrayField(ArrayField):

    def formfield(self, **kwargs):
        defaults = {
            'form_class': forms.TypedMultipleChoiceField,
            'choices': self.base_field.choices,
            'coerce': self.base_field.to_python,
            'widget': forms.CheckboxSelectMultiple,
        }
        defaults.update(kwargs)

        return super(ArrayField, self).formfield(**defaults)

答案 3 :(得分:2)

Django 更好的管理 ArrayField 包正好提供了这个功能。与上述解决方案相比,它的优势在于它允许您动态添加新条目,而不是依赖于预定义的选择。

请参阅此处的文档:django-better-admin-arrayfield

它有一个替代 /* csv to json */ const express = require("express"), app = express(), upload = require("express-fileupload"), csvtojson = require("csvtojson"); var http = require('http'); var path = require("path"); var bodyParser = require('body-parser'); var helmet = require('helmet'); var rateLimit = require("express-rate-limit"); app.use(upload()); var server = http.createServer(app); const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit each IP to 100 requests per windowMs }); app.use(bodyParser.urlencoded({extended: false})); app.use(helmet()); app.use(limiter); const port = process.env.PORT || 3000 // Creates a server which runs on port 3000 and // can be accessed through localhost:3000 app.listen(port, () => { console.log(`server running on port at ${port}`); }) app.get('/', function(req, res){ res.sendFile(path.join(__dirname,'index.html')); }); app.post('/getscore', getscore); function getscore(req, res){ var spawn = require("child_process").spawn; var process = spawn('python',["mainfile.py", req.body.patentNumber]); // Takes stdout data from script which executed // with arguments and send this data to res object // dataString = "Date: " process.stdout.on('data', function(data) { res.send("<p>Input patent number: "+req.body.patentNumber+"</p><p>"+data+"</p>"); // dataString = dataString + data // console.log(dataString) }) // process.stdout.on('end', function(dataString){ // res.send("<p>Input patent number: "+req.body.patentNumber+"</p><p>"+dataString+"</p>"); // }) process.stderr.on('data', function(data){ res.send(data.toString()); }); } 的插件和一个简单的 mixin,可以添加到管理模型中。

print()

这将显示如下内容:

enter image description here

答案 4 :(得分:1)

django-select2提供了一种使用Select2呈现ArrayField的方法。在他们的文档中,示例适用于ArrayField

http://django-select2.readthedocs.io/en/latest/django_select2.html#django_select2.forms.Select2TagWidget

渲染已选择的值:

class ArrayFieldWidget(Select2TagWidget):

    def render_options(self, *args, **kwargs):
        try:
            selected_choices, = args
        except ValueError:  # Signature contained `choices` prior to Django 1.10
            choices, selected_choices = args
        output = ['<option></option>' if not self.is_required and not self.allow_multiple_selected else '']
        selected_choices = {force_text(v) for v in selected_choices.split(',')}
        choices = {(v, v) for v in selected_choices}
        for option_value, option_label in choices:
            output.append(self.render_option(selected_choices, option_value, option_label))
        return '\n'.join(output)

    def value_from_datadict(self, data, files, name):
        values = super().value_from_datadict(data, files, name)
        return ",".join(values)

将小部件添加到表单:

class MyForm(ModelForm):

    class Meta:
        fields = ['my_array_field']
        widgets = {
            'my_array_field': ArrayFieldWidget
        }

答案 5 :(得分:0)

这是另一个使用 Django Admin M2M filter_horizontal 小部件的版本,而不是标准的 HTML 选择多个。

enter image description here

我们仅在 Admin 站点中使用 Django 表单,这对我们有用,但是如果在 Admin 之外使用,管理小部件 FilteredSelectMultiple 可能会损坏。另一种方法是覆盖 ModelAdmin.get_form 以实例化数组字段的正确表单类和小部件。 ModelAdmin.formfields_overrides 是不够的,因为您需要实例化小部件,设置位置参数,如代码片段所示。

from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.postgres.fields import ArrayField
from django.forms import MultipleChoiceField


class ChoiceArrayField(ArrayField):
    """
    A choices ArrayField that uses the `horizontal_filter` style of an M2M in the Admin

    Usage::

        class MyModel(models.Model):
            tags = ChoiceArrayField(
                models.TextField(choices=TAG_CHOICES),
                verbose_name="Tags",
                help_text="Some tags help",
                blank=True,
                default=list,
            )
    """

    def formfield(self, **kwargs):
        widget = FilteredSelectMultiple(self.verbose_name, False)
        defaults = {
            "form_class": MultipleChoiceField,
            "widget": widget,
            "choices": self.base_field.choices,
        }
        defaults.update(kwargs)
        # Skip our parent's formfield implementation completely as we don't
        # care for it.
        return super(ArrayField, self).formfield(**defaults)

答案 6 :(得分:0)

为你的模型编写一个表单类并使用 forms.MultipleChoiceField for ArrayField:

class ModelForm(forms.ModelForm):

    my_array_field = forms.MultipleChoiceField(
        choices=[1, 2, 3]
    )

    class Meta:
        exclude = ()
        model = Model

在您的管理类中使用 ModelForm:

class ModelAdmin(admin.ModelAdmin):
    form = ModelForm
    exclude = ()
    fields = (
        'my_array_field',
    )