Listing only usable values in OneToOneField Django

前端 未结 2 1499
被撕碎了的回忆
被撕碎了的回忆 2021-01-16 07:33

I want to list only usable items in OneToOneField not all items, its not like filtering values in ChoiceField because we need to find out only values which can be used which

相关标签:
2条回答
  • 2021-01-16 07:56

    Although this is an old topic I came across it looking for the same answer.

    Specifically for the OP:

    Adjust your BarForm so it looks like:

    class BarForm(ModelForm):
        class Meta:
            model = Bar
    
        def __init__(self, *args, **kwargs):
            super(BarForm, self).__init__(*args, **kwargs)
    
            #only provide Foos that are not already linked to a Bar, plus the Foo that was already chosen for this Bar
            self.fields['foo'].queryset = Foo.objects.filter(Q(bar__isnull=True)|Q(bar=self.instance))
    

    That should do the trick. You overwrite the init function so you can edit the foo field in the form, supplying it with a more specific queryset of available Foo's AND (rather important) the Foo that was already selected.

    For my own case

    My original question was: How to only display available Users on a OneToOne relation?

    The Actor model in my models.py looks like this:

    class Actor(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE, related_name = 'peactor')
        # lots of other fields and some methods here
    

    In my admin.py I have the following class:

    class ActorAdmin(admin.ModelAdmin):
        # some defines for list_display, actions etc here
        form = ActorForm
    

    I was not using a special form before (just relying on the basic ModelForm that Django supplies by default for a ModelAdmin) but I needed it for the following fix to the problem. So, finally, in my forms.py I have:

    class ActorForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(ActorForm, self).__init__(*args, **kwargs)
    
            #only provide users that are not already linked to an actor, plus the user that was already chosen for this Actor
            self.fields['user'].queryset = User.objects.filter(Q(peactor__isnull=True)|Q(peactor=self.instance))
    

    So here I make an ActorForm and overwrite the __init__ method.

    self.fields['user'].queryset =
    

    Sets the queryset to be used by the user formfield. This formfield is a ModelChoiceField by default for a OneToOneField (or ForeignKey) on a model.

    Q(peactor__isnull=True)|Q(peactor=self.instance)
    

    The Q is for Q-objects that help with "complex" queries like an or statement.

    So this query says: where peactor is not set OR where peactor is the same as was already selected for this actor

    peactor being the related_name for the Actor. This way you only get the users that are available but also the one that is unavailable because it is already linked to the object you're currently editing.

    I hope this helps someone with the same question. :-)

    0 讨论(0)
  • 2021-01-16 07:58

    You need something like this in the init() method of your form.

    def __init__(self, *args, **kwargs):
    
        super(BarForm, self).__init__(*args, **kwargs)
    
        # returns Bar(s) who are not in Foo(s).
        self.fields['foo'].queryset = Bar.objects.exclude(id__in=Foo.objects.all().values_list(
                'bar_id', flat=True))
    

    PS: Code not tested.

    0 讨论(0)
提交回复
热议问题