【问题标题】:Django: adding an "Add new" button for a ForeignKey in a ModelFormDjango:在 ModelForm 中为外键添加“添加新”按钮
【发布时间】:2015-03-20 00:41:05
【问题描述】:

TL;DR:如何在 ModelForm 中为 ForeignKey 添加“添加新”按钮?

加长版: 我正在为一个项目使用 Django 1.7。我的models.py中有这两个模型

class Client(models.Model):
    name = models.CharField(max_length=100)

class Order(models.Model):
    code = models.IntegerField()
    client = models.ForeignKey(Client)

[省略其他一些不相关的字段]

我正在使用 ModelForm 用新订单填充数据库,如下所示:

class OrderNewForm(forms.ModelForm):
    class Meta:
        model = Order

Django 在为客户端字段添加下拉菜单方面做得非常好,用从客户端获取的条目填充它。不过,我希望在添加相关订单的同时有一个“添加新客户”链接/按钮/添加全新客户的任何内容。

Django 管理员会自动执行此操作,添加一个“+”按钮“打开一个弹出窗口,但我找不到像上面那样在 ModelForm 中执行此操作的简单方法。我在这里阅读了很多问题并在其他地方链接,但没有什么真正帮助我。对此有什么想法吗?

【问题讨论】:

    标签: python django django-forms


    【解决方案1】:

    我已经在自定义小部件中解决了它。我不记得我是从 Django 管理员那里获得的,还是我从头开始构建的。

    所以表格将是:

    class OrderNewForm(forms.ModelForm):
    
       client = forms.ModelChoiceField(
           required=False,
           queryset=Client.objects.all(),
           widget=RelatedFieldWidgetCanAdd(Client, related_url="so_client_add")
                                    )
       class Meta:
           model = Order
           fields = ('code', 'client')
    

    呈现“+”按钮并链接到管理界面中的添加弹出窗口或您使用 related_url 参数提供的自定义视图的小部件是:

    from django.core.urlresolvers import reverse
    from django.utils.safestring import mark_safe
    from django.forms import widgets
    from django.conf import settings
    
    class RelatedFieldWidgetCanAdd(widgets.Select):
    
        def __init__(self, related_model, related_url=None, *args, **kw):
    
            super(RelatedFieldWidgetCanAdd, self).__init__(*args, **kw)
    
            if not related_url:
                rel_to = related_model
                info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
                related_url = 'admin:%s_%s_add' % info
    
            # Be careful that here "reverse" is not allowed
            self.related_url = related_url
    
        def render(self, name, value, *args, **kwargs):
            self.related_url = reverse(self.related_url)
            output = [super(RelatedFieldWidgetCanAdd, self).render(name, value, *args, **kwargs)]
            output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
                (self.related_url, name))
            output.append(u'<img src="%sadmin/img/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.STATIC_URL, _('Add Another')))                                                                                                                               
           return mark_safe(u''.join(output))
    

    【讨论】:

    • 这是一个很好的提示,它就像一个魅力。我正在使用 Python3,所以我需要一些非常小的调整来删除 unicode 和旧式超级,但这正是我所需要的。我现在只需要在 render() 中进行更多调整,使其打开模式弹出窗口并在添加新客户端后重新加载查询集。
    • 这似乎是最好的方法。我遇到了django-ajax-select 包的 AutoCompleteField 的类似问题,最终将自定义小部件和自定义字段子类化。
    【解决方案2】:

    对于python3:

    class RelatedFieldWidgetCanAdd(widgets.Select):
    
        def __init__(self, related_model, related_url=None, *args, **kw):
    
            super(RelatedFieldWidgetCanAdd, self).__init__(*args, **kw)
    
            if not related_url:
                rel_to = related_model
                info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
                related_url = 'admin:%s_%s_add' % info
    
            # Be careful that here "reverse" is not allowed
            self.related_url = related_url
    
        def render(self, name, value, *args, **kwargs):
            self.related_url = reverse(self.related_url)
            output = [super(RelatedFieldWidgetCanAdd, self).render(name, value, *args, **kwargs)]
            output.append('<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
                (self.related_url, name))
            output.append('<img src="%sadmin/img/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.STATIC_URL, 'Add Another'))
            return mark_safe(''.join(output))
    

    【讨论】:

    • 我在 widget=RelatedFieldWidgetCanAdd(MyModel, related_url="")` 中输入了空白文本 related_url="" 它返回 Reverse for '/admin/products/maingroup/add/' not found. '/admin/products/maingroup/add/' is not a valid view function or pattern name.
    【解决方案3】:

    并添加到 RelatedFieldWidgetCanAdd 功能以直接向字段添加新值添加“?_to_field=id&_popup=1”到 url... 因此在 python3 中(感谢 Cyril):

    class RelatedFieldWidgetCanAdd(widgets.Select):
    
    def __init__(self, related_model, related_url=None, *args, **kw):
    
        super(RelatedFieldWidgetCanAdd, self).__init__(*args, **kw)
    
        if not related_url:
            rel_to = related_model
            info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
            related_url = 'admin:%s_%s_add' % info
    
        # Be careful that here "reverse" is not allowed
        self.related_url = related_url
    
    def render(self, name, value, *args, **kwargs):
        self.related_url = reverse(self.related_url)
        output = [super(RelatedFieldWidgetCanAdd, self).render(name, value, *args, **kwargs)]
        output.append('<a href="%s?_to_field=id&_popup=1" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
            (self.related_url, name))
        output.append('<img src="%sadmin/img/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.STATIC_URL, 'Add Another'))
        return mark_safe(''.join(output))
    

    【讨论】:

    • 谢谢您,我有一个问题,此代码仅显示属性名称等字段的名称,这不使用详细名称,并且我在代码中使用了 3 种语言的国际化,知道如何为标签使用 verbose_name 吗?谢谢!
    • 嗯...不知道问题出在哪里,对我来说,它将详细名称(可翻译)显示为字段标签,选择框本身,然后是带有“添加另一个”的 +-Icon,我希望在字段标签中使用详细名称,就是这种情况。尝试更改字段的详细名称(不是相关模型)并检查它是否显示在字段标签中...
    • stackoverflow.com/questions/69313268/…你能看看这个问题吗
    猜你喜欢
    • 1970-01-01
    • 2019-02-07
    • 2011-08-08
    • 2021-04-04
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 2016-02-11
    • 1970-01-01
    相关资源
    最近更新 更多