Django:将模型选择字段选项设置为queryset值和其他值

时间:2019-12-21 23:14:51

标签: django django-models django-forms

在以下Django模型中

class Post(models.Model):
    author = models.ForeignKey(CustomUser, on_delete=models.CASCADE,)
    title = models.CharField(max_length=200, null=True)
    text = models.TextField()
    post_url = models.URLField(max_length = 200, blank=True)
    post_type = models.IntegerField()
    slug = models.SlugField(unique=True, blank=True)

class Tiers(models.Model):
    user = models.ForeignKey(CustomUser, default=None, null=True, on_delete=models.CASCADE,)
    tier_name = models.CharField(max_length=128, blank=True) 
    tier_value = models.IntegerField() 
    slug = models.SlugField(unique=True, blank=True)

我想对下面的表格使用帖子模型

class PostForm(forms.ModelForm):
    class Meta:
        model = models.Post
        fields = ('title', 'text', 'post_url', 'post_type')

但是对于 post_type 字段,我想显示为下拉菜单,其中包含Tiers模型 tier_value 的选项。例如,如果user1在Tiers模型中具有3个条目,且tier_values分别为10、20和30。我想显示4个选项0、10、20和30。有人可以帮助我实现这一点吗?

1 个答案:

答案 0 :(得分:0)

理论上,您可以override the FormField django generates。在这种情况下,您可以在post_type字段中使用Select widget

但是,我认为您的建模可以得到改善/标准化,如果这样做,您的问题将得到解决。考虑以下模型:

class Tier(models.Model):
    name = models.CharField(max_length=128, blank=True) 
    value = models.IntegerField() 
    slug = models.SlugField(unique=True, blank=True)

class CustomUser(...):
    tiers = models.ManyToManyField(Tier, related_name='users')
    ...

class Post(models.Model):
    author = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    tier = models.ForeignKeyField(Tier)
    title = models.CharField(max_length=200, null=True)
    text = models.TextField()
    url = models.URLField(max_length = 200, blank=True)
    slug = models.SlugField(unique=True, blank=True)

这样,您的不同用户可以具有相同的层(这可能是您想要的?),而无需重复名称和级别值。

现在,当您使用Post创建ModelForm时,它将自动为您提供所有现有层的选择字段。但是,您只希望能够选择用户所在的层,因此可以为该字段设置自定义queryset

class PostForm(forms.ModelForm):
    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['tier'].queryset = Tier.objects.filter(users__in=[user])

    class Meta:
        model = models.Post
        fields = ('title', 'text', 'url', 'tier')

编辑:我刚刚看到您想允许用户所在的或价值较小的所有所有层。您可以用相同的方式执行此操作,只需调整查询集即可:

max_tier_value = user.tiers.aggregate(Max('value')).value__max
self.fields['tier'].queryset = Tier.objects.filter(value__lte=max_tier_value)

但是,您可能要同时执行这两个操作之一,但不能同时执行两个操作:

  • 每个用户都被分配了一个级别,并且可以创建任何较低级别的帖子。
  • 每个用户都被分配了多个级别,并且只能使用这些级别创建帖子。

因此,如果使用此查询集,则应重新建模,以使CustomUser.tiermodels.ForeignKey(Tier, related_name='users')