Model.ManyToManyField.all() gives AttributeError:

2019-08-01 16:25发布

问题:

I'm using Django 2.0.2, Python 3.6.4 and PyCharm 2017.3.3

Models: (in models.py)

class Position(models.Model):
    title = models.CharField(max_length=50)
    gang = models.ForeignKey(Gang, on_delete=models.CASCADE)
    description = models.TextField(max_length=20000)

    def __str__(self):
        return str(self.title) + ', ' + str(self.gang)

class Application(models.Model):
    positions = models.ManyToManyField(Position)
    applicant = models.ForeignKey(User, on_delete=models.CASCADE)

class Ranking(models.Model):
    position = models.ForeignKey(Position, on_delete=models.CASCADE)
    applicant = models.ForeignKey(User, on_delete=models.CASCADE)
    rank = models.IntegerField(default=3,validators=[
            MaxValueValidator(3),
            MinValueValidator(1)
        ])

Form: (in forms.py)

class RankingForm(forms.ModelForm):
    rank = forms.IntegerField(max_value=3, min_value=1)
    position = forms.ModelMultipleChoiceField(queryset=Application.positions.all())

    class Meta:
        model = Ranking
        exclude = ['applicant']
        fields = ('rank', 'position')

    def __init__(self, *args, **kwargs):
        super(RankingForm, self).__init__(*args, **kwargs)
        self.fields['rank'].widget.attrs.update({'class': 'form-control'})

I keep getting the AttributeError in RankingForm from

"position = forms.ModelMultipleChoiceField(queryset=Application.positions.all())"

When i write

class Application(models.Model):
    ... 

    def __str__(self):
        return str(self.positions.all())

it shows in django-admin as a QuerySet (which works for forms.ModelMultipleChoiceField()), but writing

    class Application(models.Model):
    ... 

    def __str__(self):
        return str(Application.positions.all())

gives me the same error: 'ManyToManyDescriptor' object has no attribute 'all'

Writing

    class RankingForm(forms.ModelForm):
        ...
        position = forms.ModelMultipleChoiceField(queryset=Position.objects.all())

works, but this is not what i want the field to display.

I want to make a ModelMultipleChoiceField() with all the positions from a specific application, but this error keeps getting in the way. It seems that just referencing a model doesn't work, but referencing self does?? Any help is greatly appreciated! :)

Btw, I haven't found any good documentation on this problem, but this seems to be the code for related_descriptors.py where ManyToManyDescriptor is located

回答1:

Evaluating relationships are done with an instance that is an initialized instance of the class.

An instance of the Application.

application = Application.objects.first()
application.positions.all()

Change the form queryset after initialization.

class RankingForm(forms.ModelForm):
    rank = forms.IntegerField(max_value=3, min_value=1)
    position = forms.ModelMultipleChoiceField(queryset=Positions.objects.none())

    class Meta:
        model = Ranking
        exclude = ['applicant']
        fields = ['rank', 'position']

    def __init__(self, *args, **kwargs):
        super(RankingForm, self).__init__(*args, **kwargs)
        self.fields['rank'].widget.attrs.update({'class': 'form-control'})  
        self.fields['position'].queryset = self.instance.positions.all()


回答2:

You can access the current instance of your model that the ModelForm object is working with using the instance attribute. You can then use it to create the correct queryset in __init__:

class RankingForm(forms.ModelForm):
    ...

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['rank'].widget.attrs.update({'class': 'form-control'})
        self.fields['position'].queryset = self.instance.positions.all()