Get request.user in queryset in a ModelForm with a ModelChoiceField that contains a RadioSelect widget
Asked Answered
M

1

6

I would like to ask the community the following regarding passing request.user to a queryset in ModelForm. My ModelForm is:

class goForm(ModelForm): 
    user_choice = goModelChoiceField(
            widget=forms.RadioSelect,
            queryset=Document.objects.all().filter(who_upload=request.user),
            empty_label=None,
            to_field_name='docfile',
            label = 'Please select'
            )            
    class Meta:
        model = go
        fields = ['user_choice']

With

class goModelChoiceField(forms.ModelChoiceField):
        def label_from_instance(self, obj):
            return  "'%s' uploaded on %s" % (obj.file_name, 
                obj.when_upload.date()) 

All the answers I have found refer to either passing request.user to __init__ or populate the goForm in the view with the filtered selection. However nothing seems to work in my case since I have sub-classed the form to return a specific string and also I am using the RadioSelect widget that specifically needs the queryset as an argument (I am not 100% sure about that). So how can I pass request.user in user_choice ?

Mylesmylitta answered 20/7, 2015 at 13:36 Comment(7)
Doing it in __init__ is exactly the right solution, and the fact that you have a custom class is irrelevant. Please show what you have tried and the errors you got.Telemotor
Please check the indentation of your code. Putting the goModelChoiceField inside your ModelForm is probably a bad idea (it makes the code confusing and stops you reusing it in other classes). Having user_choice defined inside goModelChoiceField is certainly wrong.Hausmann
@Hausmann : yes I realized that, I moved user_choice outside goModelChoiceForm and inside Meta class. It is strange though how it works in both ways. Thank you.Mylesmylitta
The user_choice field shouldn't be defined in the Meta class either.Hausmann
@Hausmann : It is the only place where the code works. If it is placed outside the Meta class then I would have to also move out the goModelChoiceField subclass and make it a class by itself. But this wouldn't make my code more sparse? I am asking... I am not experienced as you or @Daniel Roseman of course. Thank you again for your patience and time you spend. I appreciate that.Mylesmylitta
No, it won't work at all if it is in Meta: Django does not expect fields to be defined there. And you should make goModelChoiceField into a standalone class; nested classes offer no benefit in Python. Neither of these things has any impact on how you pass request.user, which still needs to be done in __init__.Telemotor
@Daniel Roseman : Thank you, I will try again with __init__. Thank youMylesmylitta
H
9

The model choice field docs show how you can set the queryset in the __init__ method.

class goForm(ModelForm): 
    user_choice = goModelChoiceField(
        widget=forms.RadioSelect,
        queryset=None,
        empty_label=None,
        to_field_name='docfile',
        label = 'Please select'
        )            

    def __init__(self, user, *args, **kwargs):
        super(goForm, self).__init__(*args, **kwargs)
        self.fields['user_choice'].queryset = Document.objects.all().filter(who_upload=user)

    class Meta:
        model = go
        fields = ['user_choice']

Note that the __init__ method takes the user as an argument now, so remember to pass it wherever you instantiate your form in your view. For example:

form = goForm(user=request.user, data=request.POST)
Hausmann answered 20/7, 2015 at 15:13 Comment(4)
Glad it worked for you. One last bit of advice - I recommend renaming your model to Go, your form to GoForm etc. Django models are usually capitalized and the lowercase form is used for an individual instance, for example go = Go.objects.get(id=10).Hausmann
Thank you very much Alasdair. If I may ask another question. Your solution although seems to render fine when there is a GET, when there is a POST it either gives a non-keyword arg after keyword arg when the sequence of the arguments is the one you gave in your solution (i.e.: (user=request.user, data=request.POST)` and by swapping the order it gives a __init__() got multiple values for keyword argument 'user'. I suspect I should keep the first order but how can I escape this error? Thanks again Alasdair.Mylesmylitta
It seems that the solution to the above is not to change the signature of the __init__ but get user with user = kwargs.pop('user', None) (see this for a reference), that is why I edited your answer.Mylesmylitta
You can make the code work by either including user in the __init__ signature, or popping it from kwargs. You shouldn't get a non-keyword arg after keyword arg with my code if you provide all arguments as keyword arguments.Hausmann

© 2022 - 2024 — McMap. All rights reserved.