【问题标题】:Choose only one item related by foreign key inline仅选择外键内联相关的一项
【发布时间】:2020-07-30 05:47:06
【问题描述】:

我有两个基本模型,产品和图像,后者通过外键分配给产品:

class Image(models.Model):
    name = models.CharField(max_length=100)
    image = models.ImageField(upload_to=sku_dir)
    description = models.CharField(max_length=100, default='', blank=True)
    product = models.ForeignKey(product.Product, on_delete=models.CASCADE, related_name='images')

在管理员中,我使用内联图像模型:

class ImageInline(admin.TabularInline):
    model = image.Image
    extra = 1

@admin.register(product.Product)
class ProductAdmin(admin.ModelAdmin):
    search_fields = ['sku', 'name']
    list_display = ['sku', 'name', 'price']
    autocomplete_fields = ['genus']
    inlines = [ImageInline,]

我想在图像中添加一个额外的字段来控制默认显示哪个图像 - 理想情况下,产品管理表单上的每个图像都会有一个单选按钮,并且一次只能选择一个图像.

由于我仍在学习 Django,我觉得有一种简单的方法可以做到这一点,但我不知道 Django 特定的正确术语来搜索答案(类似于我花了一段时间才发现“内联" 是一个术语,用于在另一个模型中显示一个模型的形式)。

如何为每个图像添加一个单选按钮,只允许在内联表单中选择一个图像?

【问题讨论】:

  • @Jim 我想我认为这将是一个现有的模式,但它似乎不是。我想避免做任何非常规的事情,单选按钮内置了“防白痴”,但我想我只需要使用 IntegerField 作为位置值,这很容易出现用户输入错误,但可以用开箱即用的功能。同样有趣的是,我搜索了一页又一页的 SO 问题,但从未看到您链接的那个。
  • 我会稍微琢磨一下,看看能不能想出点什么……

标签: django django-models


【解决方案1】:

我能想到的获得类似功能的一种方法(尽管不完全是您正在寻找的功能)是在您的产品模型中添加一个名为default_image 的新字段。您将此字段设置为 Image 模型的 ForeignKey(您需要使用 related_name 以避免反向查询名称冲突)。您为 Product 模型使用自定义表单,将 default_image 字段的查询集限制为与特定产品关联的图像。

在管理面板中,您将有一个下拉菜单(或单选按钮,如果您使用单选小部件)来选择该产品的默认图像 - 选择将是找到的与该产品相关的图像,这将允许您为特定产品选择一个(并且只有一个)默认图像。

我设置了一个快速测试(使用Django tutorial 中的模型以避免误解您的设置):

models.py:

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)

    # this field will hold the preferred choice for this question - 
    # setting one is optional
    preferred_choice = models.ForeignKey('Choice', on_delete=models.CASCADE, related_name='preferred_choice')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)

admin.py:

from django.contrib import admin
from django import forms
from .models import Question, Choice


# Setup a custom form
class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = "__all__"


    # call super and then set the queryset of choices to be the ones related to 
    # the question we are working on
    def __init__(self, *args, **kwargs):
        super(QuestionForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['preferred_choice'].queryset = Choice.objects.filter(question=self.instance)


class ChoiceInline(admin.TabularInline):
    model = Choice


class QuestionAdmin(admin.ModelAdmin):
    form = QuestionForm
    inlines = [ChoiceInline]


admin.site.register(Question, QuestionAdmin)

另一种可能的解决方案是将布尔/复选框字段添加到名为“default_image”的图像模型中,但您必须实现某种自定义保存逻辑以确保只有一个图像将该字段设置为 True

这是this question 中的处理方式,而 JS 习惯于 1) 使复选框看起来像单选按钮,以及 2) 确保其他选项在设置为 True 时设置为 False

不是完美的解决方案,但希望这至少可以帮助您指明正确的方向...

【讨论】:

  • 我考虑了您的第一个建议,但我不喜欢的是您必须使用新图像保存模型,然后再次对其进行编辑以获取选择输入以重新填充。其次,添加一堆自定义表单和JS不是我这个时候想走的路。除非出现其他更好的东西,否则我想我只会为每个图像使用标记为“位置”的IntegerField,并将模型ordering 设置为该字段。
【解决方案2】:

我之前一直在为同样的问题苦苦挣扎,最终我将默认字段(布尔值)添加到图像模型中,该字段只能应用于每个产品的一个图像。

我还添加了 order_by 字段(整数)来控制首先显示哪个图像。

当然,您可以在前端添加 Javascript 以简化此过程,例如:

  1. 点击内嵌图片可以很好地使用 CSS 进行默认设置
  2. 点击向上/向下箭头重新排列图片

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-08
    • 1970-01-01
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    相关资源
    最近更新 更多