Django modelform and passing extra parameter - django

I want to populate my modelform to include the IP address, however I'm not able to set the ip as I want, it simply returns "This field is required" for IP address.
models.py
class SignUp(models.Model):
....
ip = models.IPAddressField()
views.py
def my_view(request):
form = SignUpForm(request.POST, ip_addr=get_client_ip(request))
forms.py
class SignUpForm(ModelForm):
class Meta:
model = SignUp
def __init__(self, *args, **kwargs):
self.ip_addr = kwargs.pop('ip_addr', None)
super(SignUpForm, self).__init__(*args, **kwargs)
def clean_ip(self):
return self.ip_addr

You haven't actually set an initial value for the ip form field meaning it's empty. Try:
def __init__(self, *args, **kwargs):
super(SignUpForm, self).__init__(*args, **kwargs)
self.fields['ip'].initial = kwargs.pop('ip_addr', None)
The clean_ip method is for validation upon submission of the form and is only called after form.is_valid() is called. It should check that the value of the field is a valid ip

I don't think you should use clean_ip here. You are not cleaning it anyhow. Either use initial in your view or override save method.

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)

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

ModelForm User Mixin

I've got some models with user field.
For this purpose I'd like to create a form mixin that would add self.user instance (which is provided to the form in views). Is it possible ?
Here's the example
class UserFormMixin(object):
"""Removes user instance from kwargs and adding it to object"""
def __init__(self, *args, **kwargs):
super(UserFormMixin, self).__init__(*args, **kwargs)
self.user = kwargs.pop('user')
def save(self, **kwargs):
obj = super(UserFormMixin, self).save(commit=False)
obj.user = self.user
if kwargs['commit']:
return obj.save()
else:
return obj
What I'd like to achieve:
class SomeFormWithUserField(UserFormMixin, ModelForm):
class Meta:
model = SomeModelWithUserField
fields = ['fields without user']
def save(self, **kwargs):
data = super(SomeFormWithUserField, sefl).save(commit=False)
#data already with user prepended
#do some other stuff with data
if kwargs['commit']:
return data.save()
else
return data
class SomeOtherFormWithUser(UserFormMixin, ModelForm):
class Meta:
model = SomeOtherModel
fields = ['some fields without user']
# no need to save here.. standard model form with user prepended on save()
The problem is that UserFormMixin doesn't know about model instance? Or am I wrong here?
I am getting some problems.. like 'commit' kwargs key error.. or object is not saved..
You're close, you just have some logic errors. First, in order to override ModelForm methods, your mixin needs to inherit from ModelForm.
class UserFormMixin(forms.ModelForm):
...
Then, any forms that inherit from it just inherit UserFormMixin, not ModelForm.
class SomeOtherFormWithUser(UserFormMixin):
...
Second, your __init__ method override is incorrect. You need to accept any and all args and kwargs that get passed into it.
def __init__(self, *args, **kwargs):
...
Finally, don't override the save method again, in the subclass. I guess it won't technically hurt anything, but what's the point of inheritance if you're going to repeat code, anyways? If user is not nullable, you can always add an if block to check if self.user is not None before adding it to the model. Of course, if user is not nullable, your model won't likely save without self.user anyways.
This one seems to work fine. Thanks Chris!
If this can be coded better please let me know.
class UserFormMixin(forms.ModelForm):
"""Removes user instance from kwargs and adding it to object"""
def __init__(self, *args, **kwargs):
super(UserFormMixin, self).__init__(*args, **kwargs)
self.user = kwargs.pop('user')
def save(self, commit=True):
obj = super(UserFormMixin, self).save(commit=False)
obj.user = self.user
if commit:
return obj.save()
else:
return obj
class SomeFormWithUserField(UserFormMixin):
class Meta:
model = SomeModelWithUserField
fields = ['fields without user']
def save(self, **kwargs):
data = super(SomeFormWithUserField, sefl).save(commit=False)
#data already with user prepended
#do some other stuff with data
# self.send_mail() f.e.
return data.save()
class SomeOtherFormWithUser(UserFormMixin):
class Meta:
model = SomeOtherModel
fields = ['some fields without user']
# this will work too

Django custom selectfield with initial

I have a selectfield which I override a charfield in my model. I can't use a foreignkey at all on the field.
class AliasForm(forms.ModelForm):
account = forms.ModelChoiceField(queryset=Account.objects.all(), widget=forms.HiddenInput())
domain = forms.ModelChoiceField(queryset=Domain.objects.all(), widget=forms.HiddenInput())
end = forms.ModelChoiceField(queryset=Mailbox.objects.all())
def __init__(self, *args, **kwargs):
self.account = kwargs.pop('account', None)
super(AliasForm, self).__init__(*args, **kwargs)
if self.account:
self.fields['end'].queryset = Mailbox.objects.filter(account=self.account)
How can I make end get passed in a value where it is autoselected?
Ok figured it out.
I needed to pass in initial but with instance.id and not instance
form = AliasForm(initial={'end': mailbox.id})
I was doing just 'end': mailbox

Django Forms clean() method - need IP address of client

I am overriding the clean() method on a Django form. I want to have access to the IP address of the client (assuming this is a bound form). If I had a reference to the request object, I could get it easily from META("REMOTE_ADDR"). However, I do not have a reference to the request.
Any ideas on how this could be done?
So give yourself a reference to it.
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(MyModelForm, self).__init__(*args, **kwargs)
def clean(self):
ip_address = self.request['META']['REMOTE_ADDR']
and in your view:
myform = MyModelForm(request.POST, request=request)