Django forms request.user - django

I my model users can create rifles and this rifle is obviously associated with a User.
class Gun(ImageModel):
user = models.ForeignKey(User)
...
...
...
I have another model which is dependent on this and need to make use of the users rifles, but when the user adds a record I only want to display his rifles.
mt model looks as follows
class Trophies(ImageModel):
used_his = models.ForeignKey(Gun)
my form looks as follows
from django.forms import ModelForm
from django import forms
from models import Trophies
from gunsafe.models import Gun
from django.contrib.auth.models import User
class TrophiesForm(request.user, ModelForm):
used_his = forms.ModelMultipleChoiceField(queryset=Gun.objects.filter(user__id=1))
def __init__(self, user, *args, **kwargs):
super(TrophiesForm, self).__init__(*args, **kwargs)
self.fields['used_his'].queryset = User.objects.filter(pk = user)
I was wondering how I can get the current logged in users ID instead of the user__id=1
Here is the view.
def edit(request, trophy_id, template_name='trophies/edit.html'):
trophy = Trophies.objects.get(pk=trophy_id)
if request.method == 'POST':
form = TrophiesForm(request.POST, request.FILES, instance=trophy)
if form.is_valid():
newform = form.save(commit=False)
newform.user = request.user
newform.save()
...
...

I think you can achieve this by overriding the __init__() method of the form, passing in an instance of User and filtering the queryset using that user. Something like this:
class TrophiesForm(ModelForm):
used_his = forms.ModelMultipleChoiceField(queryset=Gun.objects.filter(user__id=1))
def __init__(self, user, *args, **kwargs):
super(TrophiesForm, self).__init__(*args, **kwargs)
self.fields['used_his'].queryset = User.objects.filter(pk = user.id)
In your view you can pass in the appropriate (currently logged in) instance of User.
def my_trophies(request, *args, **kwargs):
user = request.user
form = TrophiesForm(user)
...

Another angle to Manoj's submission ...
use a kwarg to pass user data, as to not mess with the method signature since it expects request.POST as the first argument. A better convention would be as follows.
class TrophiesForm(ModelForm):
def __init__(self, *args, **kwargs):
#using kwargs
user = kwargs.pop('user', None)
super(TrophiesForm, self).__init__(*args, **kwargs)
self.fields['used_his'].queryset = User.objects.filter(pk = user.id)
Now in the call, this is more explicit and follows a better signature convention
form = TrophiesForm(request.POST, request.FILES, user=request.user)
You could also use instance: (note the super before you grab the instance obj)
class TrophiesForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SubmissionUploadForm, self).__init__(*args, **kwargs)
user = self.instance.user
self.fields['used_his'].queryset = User.objects.filter(pk = user.id)
You can then call in your views.py like so:
form = TrophiesForm(instance=*MyModel*(user=request.user))

Related

How to load a form with options from a queryset in Django

I am trying to load a form with user payment options, so this is needing a query set from the users profile.
I have tried initializing the form (below code) with user being required. The issue is if I make self.options when I am initializing. I have also tried creating the choice_field
class ListPaymentOptionsForm(forms.Form):
choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=options)
def __init__(self, user, *args, **kwargs):
self.options = list(UserPaymentOption.objects
.values_list('last_four', 'last_four')
.filter(user=user, active=True))
super(ListPaymentOptionsForm, self).__init__(self, *args, **kwargs)
The above code gives this error:
choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=options)
NameError: name 'options' is not defined
Then I have tried adding the options on the view instead like this
form = ListPaymentOptionsForm(user=request.user)
form.fields['choice_field'].choices = list(UserPaymentOption.objects
.values_list('id', 'last_four')
.filter(user=request.user, active=True))
This causes an error with the form being used on post, it seems like because it is trying to validate the value provided is a choice but in the actual form the choice is not set. The reason I believe this is the problem is this is what the form returns as
form=ListPaymentOptionsForm(request.POST)
print(form)
This returns: Choice field:Select a valid choice. 54 is not one of the available choices.
Any input on this would be very appreciated. Thanks.
Nearly there!
Try doing the fields['choice_field'].choices in the constructor.
class ListPaymentOptionsForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs) # assuming python 3 constructor
self.options = list(UserPaymentOption.objects.values_list('last_four', 'last_four').filter(user=user, active=True))
self.fields['choice_field'] = forms.ChoiceField(widget=forms.RadioSelect, choices=self.options)
Maybe consider having a look at ModelChoiceField instead however, that way you can specify a queryset instead of having to worry about creating a list:
class ListPaymentOptionsForm(forms.Form):
choice_field = forms.ModelChoiceField(widget=forms.RadioSelect, queryset=UserPaymentOption.objects.none())
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['choice_field'].queryset = UserPaymentOption.objects.filter(user=user, active=True)
EDIT based on comments we can use the kwargs to pass the user which may be better:
class ListPaymentOptionsForm(forms.Form):
choice_field = forms.ModelChoiceField(widget=forms.RadioSelect, queryset=UserPaymentOption.objects.none())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user') # this must be done before super()
super().__init__(*args, **kwargs)
self.fields['choice_field'].queryset = UserPaymentOption.objects.filter(user=user, active=True)
Then instantiate the form to handle POST data:
form = ListPaymentOptionsForm(request.POST, user=user)

Add a field value outside form in Django

Whenever I have to add a value to the instance of a form obtained from the context or from the URL I do it in the following way, using form.instance.
class PreguntaForm(forms.ModelForm):
class Meta:
model = Pregunta
fields = ('etiqueta', 'grupo', 'tipo_pregunta', 'opciones', 'mostrar_tabla', 'activo')
def __init__(self, *args, **kwargs):
cuestionario = kwargs.pop('cuestionario', False)
super(PreguntaForm, self).__init__(*args, **kwargs)
self.fields['grupo'].queryset = Grupo.objects.filter(cuestionario=cuestionario)
class PreguntaNueva(InfoPregunta, CreateView):
form_class = PreguntaForm
encabezado = 'Nueva Pregunta'
model = Pregunta
def get_form_kwargs(self):
kwargs = super(PreguntaNueva, self).get_form_kwargs()
kwargs['cuestionario'] = self.dame_cuestionario()
return kwargs
def form_valid(self, form):
form.instance.cuestionario = self.dame_cuestionario()
return super(PreguntaNueva, self).form_valid(form)
The problem that arises now is that I want to perform a check CreateView and EditView. To DRY, I want to do it in the clean method of the model, but the value that I assign to form.instance.cuestionario, is not available within the clean method. How could I do it? This value must not be edited by the user in any case.
Yes it is, you pass it in via get_form_kwargs; you just need to assign it to an instance variable in the form's __init__.
def __init__(self, *args, **kwargs):
self.cuestionario = kwargs.pop('cuestionario', False)
super(PreguntaForm, self).__init__(*args, **kwargs)
self.fields['grupo'].queryset = Grupo.objects.filter(cuestionario=self.cuestionario)
def clean(self):
# do something with self.cuestionario

django custom form is invalid due to form init

I have the following form:
class PlayerAchievementForm(forms.ModelForm):
class Meta:
model = PlayerAchievement
fields = ('achievement',)
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)
I have the following implementation in a view:
def points(request, action, target=None):
if request.method == 'POST':
if target == 'player':
form = PlayerAchievementForm(request.POST)
print form.errors
if form.is_valid():
print 'valid'
elif:
print 'invalid'
On submit, this prints invalid.
If I take out this line in the form:
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)
Then it saves without issue. What is wrong with the init?
I found the answer here: DJango form with custom __init__ not validating
I was missing:
super(PlayerAchievementForm, self).__init__(*args, **kwargs)
I had the same problem and after a lot of tries, the code that work for me is something like that:
(Notice the first instantiation of the the field at class level.)
class PlayerAchievementForm(forms.ModelForm):
achievement = Achievement.objects.filter(input_type=0)
class Meta:
model = PlayerAchievement
fields = ('achievement',)
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)

Django: Current User Id for ModelForm Admin

I want for filter a ModelChoiceField with the current user. I found a solution very close that I want to do, but I dont understand
Django: How to get current user in admin forms
The answer accepted says
"I can now access the current user in my forms.ModelForm by accessing self.current_user"
--admin.py
class Customer(BaseAdmin):
form = CustomerForm
def get_form(self, request,obj=None,**kwargs):
form = super(Customer, self).get_form(request, **kwargs)
form.current_user = request.user
return form
--forms.py
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.filter(tenant=????))
class Meta:
model = fa_customers
How do I get the current user on modelchoice queryset(tenant=????)
How do I call the self.current_user in the modelform(forms.py)
Override __init__ constructor of the CustomerForm:
class CustomerForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['default_tax'].queryset =
fa_tax_rates.objects.filter(tenant=self.current_user))
Queryset in the form field definition can be safely set to all() or none():
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.none())
Just to sum up the solution because it was very hard for me to make this work and understand the accepted answer
In admin.py
class MyModelForm (forms.ModelForm):
def __init__(self, *args,**kwargs):
super (MyModelForm ,self).__init__(*args,**kwargs)
#retrieve current_user from MyModelAdmin
self.fields['my_model_field'].queryset = Staff.objects.all().filter(person_name = self.current_user)
#The person name in the database must be the same as in Django User, otherwise use something like person_name__contains
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
def get_form(self, request, *args, **kwargs):
form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user #get current user only accessible in MyModelAdminand pass it to MyModelForm
return form

ModelMultipleChoiceField passing request.user

I want to use forms.ModelMultipleChoiceField in a form. I know it takes a queryset, however the query set I will be using take the param user which I normally pass in a view using request.user. However this is in a form, how do I pass request.user? do I need to?
Entry.objects.filter(request.user)
You should override your form's init method:
class MyForm(forms.ModelForm):
class Meta:
model = Entry
def __init__(self, user=None, *args, **kwargs):
super(MyForm, self).__init__(*args,**kwargs)
if user is not None:
form_choices = Entry.objects.filter(user)
else:
form_choices = Entry.objects.all()
self.fields['my_mfield'] = forms.ModelMultipleChoiceField(
queryset=form_choices
)
and in your views, when it's time to instantiate the form:
form = MyForm(request.user)
or
form = MyForm()