Django: Access request.GET in form to pass queryset as choices - python

How in Django i can access requset in form?
I need this to get data tuple to pass in choices to form.
Below init approach doesn't work: NameError: name 'request' is not defined, with self or without: self.request.GET.get('project') or request.GET.get('project')
class PostfilterForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(PostfilterForm, self).__init__(*args, **kwargs)
monitoring_words_to_show = Nlpmonitorword.objects.filter(monitoringwords__name = self.request.GET.get('project'))
words_list = []
for word in monitoring_words_to_show:
words_list.append((word.monitor_word, word.monitor_word))
words_list = tuple(words_list) # trying to get here tuple to pass in choises (('vk', 'vk'), ('fb', 'fb'), ('vkfb', 'vkfb'))
project = forms.CharField(required=True, label='')
monitor = forms.MultipleChoiceField(widget=forms.SelectMultiple, choices=words_list, required=False, label='')

All the code you're trying to use isn't used within a method which means it doesn't belong to any instance of a PostFilterForm and therefore has no knowledge of self let alone its fields.
You should include these in a function, although what function that should be is unclear.
def my_function(self):
monitoring_words_to_show = Nlpmonitorword.objects.filter(monitoringwords__name = self.request.GET.get('project'))
words_list = []
for word in monitoring_words_to_show:
words_list.append((word.monitor_word, word.monitor_word))
words_list = tuple(words_list) # trying to get here tuple to pass in choises (('vk', 'vk'), ('fb', 'fb'), ('vkfb', 'vkfb'))

What your form needs is not the request it's the project. It's better to deal with the request in the view and pass the required parameters to the form:
Form:
class PostfilterForm(forms.Form):
def __init__(self, project, *args, **kwargs):
self.project = project
View:
project = request.GET.get('project')
form = PostfilterForm(project, request.POST)

Related

Pass additional attribute to django-filter

I'm using django-filter together with DRF. I have a favourite-model, which is linked to several other models through a GenericRelation.
To filter for entries which have a favourite-flag, I've created a custom FavouriteFilter, which I add to the respective model. I would like to query for the content_type_id of the respective model in order to limit the results from Favourite. However, I don't know how I can pass down the model to the filter-method in the FavouriteFilter.
Here's a code snippet to illustrate the issue:
class ProjectFilter(BaseFilter):
favourite_only = FavouriteFilter()
class FavouriteFilter(django_filters.BooleanFilter):
"""
A custom filter which returns a users favourites of an element
"""
def __init__(self, *args, **kwargs):
# gettext_lazy breaks the OpenAPI generation => use gettext instead
kwargs['label'] = gettext("My favourites")
super(FavouriteFilter, self).__init__(*args, **kwargs)
def filter(self, qs, value):
if value == True:
user = get_current_user()
content_type = ContentType.objects.get_for_model(<model>)
return qs.filter(pk__in=Favourite.objects
.filter(owner_id=user)
.filter(content_type_id=content_type)
.values_list('object_id', flat=True)
)
else:
return qs
In this example, the <model>-attribute is missing. How can I pass down this information from Project to the filter?
Keyword arguments can be passed down to the filter, but they need to be removed from the kwarg-dict before the super()-method is called. Otherwise they get passed on to the superclass, the superclass's __init__()-method doesn't know the keyword and a TypeError is thrown:
TypeError: __init__() got an unexpected keyword argument 'model'
In the example above, the superclass is django_filters.BooleanFilter respectively django_filters.Filter.
Using the dict.pop()-method, the keyword is removed from the kwargs-dictionary and at the same time we can save it for further use. Since content_type never changes after initialization, it can already be set in __init__().
Here's a working example of the code above, where Project is the django-model I want to pass down to the filter:
class ProjectFilter(BaseFilter):
favourite_only = FavouriteFilter(model=Project)
class FavouriteFilter(django_filters.BooleanFilter):
"""
A custom filter which returns a users favourites of an element
"""
def __init__(self, *args, **kwargs):
# gettext_lazy breaks the OpenAPI generation => use gettext instead
kwargs['label'] = gettext("My favourites")
model = kwargs.pop('model')
self.content_type = ContentType.objects.get_for_model(model)
super(FavouriteFilter, self).__init__(*args, **kwargs)
def filter(self, qs, value):
if value == True:
user = get_current_user()
return qs.filter(pk__in=Favourite.objects
.filter(owner_id=user)
.filter(content_type_id=self.content_type)
.values_list('object_id', flat=True)
)
else:
return qs
For my specific use-case, where I'm looking for the model that is using the filter, the model is available through the queryset as qs.model. The code-snippet looks like this:
class ProjectFilter(BaseFilter):
favourite_only = FavouriteFilter()
class FavouriteFilter(django_filters.BooleanFilter):
"""
A custom filter which returns a users favourites of an element
"""
def __init__(self, *args, **kwargs):
# gettext_lazy breaks the OpenAPI generation => use gettext instead
kwargs['label'] = gettext("My favourites")
super(FavouriteFilter, self).__init__(*args, **kwargs)
def filter(self, qs, value):
if value == True:
user = get_current_user()
content_type = ContentType.objects.get_for_model(qs.model)
return qs.filter(pk__in=Favourite.objects
.filter(owner_id=user)
.filter(content_type_id=content_type)
.values_list('object_id', flat=True)
)
else:
return qs

Django forms: how to use a switch case ChoiceField / choises using request.session value?

I have 2 linked models for thesaurus with labels in English and French
I have a 2 class methods (options_list_eng and options_list_fra) that return a list of labels based on code pass in parameters: one that returns french labels and the other return English labels
I also have a request.session['language'] that catch the browser language of the user
I want to use theses methods to set choices attributes in my form depending on the language (value in request.session.get('language'))
I try use something like that but it do not works
if self.language == 'en':
TYPES = Thesaurus.options_list_eng(1)
else:
TYPES = Thesaurus.options_list_fra(1)
below my current code that runs well but does not allow french/english translation of choices
forms.py
class RandomisationForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
self.request = request
self.language = request.session.get('language')
super(RandomisationForm, self).__init__(*args, **kwargs)
TYPES = Thesaurus.options_list_eng(1)
ran_pro = forms.ChoiceField(label = _("Type"), widget=forms.Select, choices=TYPES)
You can define field in form's __init__ method like this:
class RandomisationForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
super(RandomisationForm, self).__init__(*args, **kwargs)
self.request = request
self.language = request.session.get('language')
if self.language == 'en':
TYPES = Thesaurus.options_list_eng(1)
else:
TYPES = Thesaurus.options_list_fra(1)
self.fields["ran_pro"] = forms.ChoiceField(label = _("Type"), widget=forms.Select, choices=TYPES)

Modify kwarg data for view in django

I am trying to implement an MVC like pattern in my views.
Basically I have a base View which serves a normal type, and type1 view and type2 view that extends the base view.
I also have a controller and a model to get and set data.
I need to pass this model over to the view.
But I always get a KeyError when I access the kwargs dict from the view.
I am new to django and python. So please don't mind if I am missing something obvious.
Here is the code
class Controller():
NORMAL, TYPE1 , TYPE2 = (0,1,2)
#classmethod
def controller_init(cls): #entry point to view. Called in urls.py
def view(request,slug,*args,**kwargs):
self = cls()
self.request = request
self.slug = slug
self.args = args
self.kwargs = kwargs
return self.start()
return view
def start(self):
modal = Modal()
self.kwargs['modal'] = modal
modal.init(self.slug)
ptype = modal.getPtype()
return self.showView(ptype)
def showView(self,viewType):
if(viewType == self.NORMAL):
view_handler = View1.as_view()
elif(projectType == self.TYPE1):
view_handler = ExtendedView1.as_view()
else:
view_handler = ExtendedView2.as_view()
return view_handler(self.request,self.args,self.kwargs)
Here is my view :
from django.views.generic.base import TemplateView
class View1(TemplateView):
template_name = "view_detail.html"
def get_context_data(self, **kwargs):
context = super(View1, self).get_context_data(**kwargs)
self.modal = kwargs['modal']
context['pid'] = self.modal.getPID()
context['title'] = "This is a normal"
return context
When I run the page, I get a KeyError saying the key modal is not defined.

Provide different kwargs for each form in formset

I am trying to use a formset to create forms for a set of timeframes related to dates:
class Event(models.Model):
date = models.DateField()
class TimeFrame(models.Model):
start = models.DateTimeField()
end = models.DateTimeField()
event = models.ForeignKey('Event')
I have code that gets me a queryset of timeframes for each event and added a kwarg to pass this into my form:
class SelectDatesForm(forms.Form):
timeframes = forms.ModelChoiceField(queryset=HostTimeFrame.objects.none())
def __init__(self, *args, **kwargs):
qs = kwargs.pop('timeframes')
super(SelectDatesForm, self).__init__(*args, **kwargs)
self.fields['timeframes'].queryset = qs
Now I'm trying to construct a formset that lets me show timeframes for multiple events on one page. I already found this question, explaining how to pass initial data, for serveral forms, but its not the same as passing it to a queryset.
Also there is this new function from django 1.9 but it doesnt allow me to get different querysets for each form.
UPDATE:
I got the solution from the answer working, however, whenever im running formset.is_valid() i get the error:
Select a valid choice. That choice is not one of the available
choices.
Here is what I do in my view:
timeframes = [HostTimeFrame.objects.all()]
SelectDatesFormset = formset_factory(form=SelectDatesForm, extra=len(timeframes), formset=BaseSelectDatesFormSet)
if request.method == 'POST':
formset = SelectDatesFormset(request.POST, form_kwargs={'timeframes_list': timeframes})
if formset.is_valid():
# do something with the formset.cleaned_data
print(formset)
pass
else:
formset = SelectDatesFormset(form_kwargs={'timeframes_list': timeframes})
Ive been trying for hours to find where this actual validation is done, but i cant find it for the live of me.
Edit: I tested this with the singular form, and i have the same issue, I created a new question for this here.
UPDATE:
Only partial solution, see question.
Solved it myself:
First I created a BaseFormSet:
class BaseSelectDatesFormSet(BaseFormSet):
def get_form_kwargs(self, index):
kwargs = super(BaseSelectDatesFormSet, self).get_form_kwargs(index)
kwargs['timeframes'] = kwargs['timeframes_list'][index]
return kwargs
Then I could pass the list of timeframes in the view:
SelectDatesFormset = formset_factory(form=SelectDatesForm, extra=4, formset=BaseSelectDatesFormSet)
formset = SelectDatesFormset(form_kwargs={'timeframes_list': timeframes})
Finally I had to update my form init to pop the list as well so the super constructor doesnt complain about unwanted kwargs:
def __init__(self, *args, **kwargs):
qs = kwargs.pop('timeframes')
qs_list = kwargs.pop('timeframes_list')
super(SelectDatesForm, self).__init__(*args, **kwargs)
self.fields['timeframes'].queryset = qs.order_by('start')
For peeps using Class Based View FormView along with form_class as formset or formset_factory, they can add an extra attribute as follows:
Pass form_kwargs in the get_form method by overriding it.
timeframes = [HostTimeFrame.objects.all()]
class SelectDatesView(FormView):
form_class = formset_factory(form=SelectDatesForm, extra=len(timeframes)
def get_form(self, form_class=None):
"""Override the method to add form kwargs. Returns an instance of the form to be used in this view."""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs(), form_kwargs={"timeframes": timeframes})
One can access it directly in the __init__ method's kwargs.
def __init__(self, *args, **kwargs):
super(SelectDatesForm, self).__init__(*args, **kwargs)
qs = kwargs.get('timeframes')
self.fields['timeframes'].queryset = qs.order_by('start')

how to change form variable values from view

i've been trying to send data to the following form from my view.
i need the variable (choices ) to change each time we call this form
class AnswersForm(forms.Form):
question = forms.RadioSelect
CHOICES=[('sf','asdf')]
radioButton = forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect())
view.py :
def exam_attend(request,ExamName,questionNo=0):
if ExamName:
myList = [('b','a')]
forms.AnswersForm.CHOICES=myList
form = forms.AnswersForm()
variabls = RequestContext(request,{'form':form})
return render_to_response('exam_pageAttend.html',variabls)
the problem is : i need the variable (CHOICES) from the form class to change it's value in the view class ( i know i can just use CHOICES = [('b','a')] ) but i want to change it from the view since this code is only to show the problem )
any better ideas ?
thanks in advance
You can change the field choices overriding the form init method (so that it accepts a choice parameter) or you can change the field's choices after the init, depending on your needs.
First Case would be like this:
class AnswersForm(forms.Form):
radioButton = forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect())
def __init__(self, radio_choices= None, *args, **kwargs):
super(AnswersForm, self).__init__(self, *args, **kwargs)
if radio_choices is not None:
self.fields['radioButton'].choices = radio_choices
View example:
form_instance = AnswersForm(new_choices)

Categories