Binding form to HTTP request with attributes containing '.' (dots/periods) - django

I have want to have an endpoint which is called by a third party which the parameter names they will use can't be changed. The submitted data contains parameters with the character . (e.g. hub.token) along with others.
I had wanted to create a Django form to validate the callback but how does one write a form class which will bind to the request. Without noticing the problem I tried hub.token which does not work as hub dictionary does not exist. I then tried hub_token in the hope Django would recognise the intent.
Having a period in the attribute is valid as in HTML form submission the name is taken the controls name attribute which can contain periods.
Is there an easy way to handle this situation without having to access each field in the view?
forms.py
from django import forms
class RegisterEndpointForm(forms.Form):
hub.token = forms.CharField()
views.py
...
register_request = RegisterEndpointForm(request.GET)
if register_request.is_valid()
pass
...

I don't thin you can set attributes containing dots. You could try a couple things. You could set the field as hub_token, then replace the dot with an underscore in request.GET. for example:
data = request.GET
data['hub_token'] = data['hub.token']
del data['hub.token']
register_request = RegisterEndpointForm(data)
…
You could also try setting the field dynamically on form init. For example:
class RegisterEndpointForm(forms.Form):
def __init__(self, *args, **kwargs):
super(RegisterEndpointForm, self).__init__(*args, **kwargs)
self.fields['hub.token'] = forms.CharField()

I have used the __init__ method suggested by #jproffitt but replace all keys containing periods with underscores.
class RegisterEndpointForm(forms.Form):
hub_verify_token = forms.CharField()
[...]
def __init__(self, *args, **kwargs):
super(RegisterEndpointForm, self).__init__(*args, **kwargs)
all_fields = self.data.copy()
for key, value in all_fields.iteritems():
safe_key = key.replace('.', '_')
if safe_key != key:
self.data[safe_key] = value

Related

How to remove Required Attribute From Django Form

I want to remove required attribute from HTML Form. And it should give error from server side that this field is required. Previously i was using required self.fields['group_name'].required=False. But it is not giving error for blank or null data. Then i came to know use_required_attribute, but i don't know about it and how to use it.
class GroupForm(forms.ModelForm):
use_required_attribute = False
class Meta:
model = Groups
fields = ['group_name', 'group_description', 'group_status']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Use form = GroupForm(use_required_attribute=False) when you initialize your form in your views.py.

Is it possible to limit a choicefield in a form instance in Django

I have a form which contains a choicefield called level allowing the selection of value between 0-3 who's options I want to "limit" dynamically. Is this possible?
pseudo-code for the view would contain something like this:
form = new instance of myform
allowed_level = 2
form.level maximum value =< allowed_level
Sure, you can dynamically set the validation criteria when creating the form: (beware: this isn't a complete example as I'm not sure of your setup)
class MyForm(forms.Form):
level = ...
def __init__(self, *args, **kwargs, allowed_level=None):
self.allowed_level = allowed_level
super(MyForm, self).__init__(*args, **kwargs)
def clean_level(self):
data = self.cleaned_data['level']
# Here you can play with what you want to allow
if level > self.allowed_level:
raise forms.ValidationError("A problem with level value")
return data
and just use your view normally
def my_view(request, ...):
form = MyForm(allowed_level=2)
if form.is_valid():
...

Django send key or value from the view to the form class

I am writing an Edit form, where some fields already contain data. Example:
class EditForm(forms.Form):
name = forms.CharField(label='Name',
widget=forms.TextInput(),
initial=Client.objects.get(pk=??????)) #how to get the id?
What I did for another form was the following (which does not work for the case of the previous EditForm):
class AddressForm(forms.Form):
address = forms.CharField(...)
def set_id(self, c_id):
self.c_id = c_id
def clean_address(self):
# i am able to use self.c_id here
views.py
form = AddressForm()
form.set_id(request.user.get_profile().id) # which works in the case of AddressForm
So what is the best way to pass an id or a value to the form, and that could be used in all forms for that session/user?
Second: is it right to use initial to fill in the form field the way I am trying to do it?
You need to override the __init__ method for your form, like so:
def __init__(self, *args, **kwargs):
try:
profile = kwargs.pop('profile')
except KeyError:
super(SelectForm, self).__init__(*args, **kwargs)
return
super(SelectForm, self).__init__(*args, **kwargs)
self.fields['people'].queryset = profile.people().order_by('name')
and, obviously, build your form passing the right parameter when needed :)

trying to get request.user, and then a query, in a form that overrides ModelChoiceField and is subclassed

I need to pass an instance variable (self.rank) to be used by a class variable (provider) (see the commented out line below).
Commented out, the code below works. But I'm pretty sure I shouldn't be trying to pass an instance variable up to a class variable anyway. So I'm dumbfounded as to how to accomplish my goal, which is to dynamically filter down my data in the ModelChoiceField.
As you can see, I already overrided ModelChoiceField so I could beautify the usernames. And I also subclassed my basic SwapForm because I have several other forms I'm using (not shown here).
Another way of saying what I need ... I want the value of request.user in my Form so I can then determine the rank of that user and then filter out my Users by rank to build a smaller ModelChoiceField (that looks good too). Note that in my views.py, I call the form using:
form = NewSwapForm(request.user)
or
form = NewSwapForm(request.user, request.POST)
In forms.py:
from myapp.swaps.models import Swaps
from django.contrib.auth.models import User
class UserModelChoiceField(forms.ModelChoiceField):
""" Override the ModelChoiceField to display friendlier name """
def label_from_instance(self, obj):
return "%s" % (obj.get_full_name())
class SwapForm(forms.ModelForm):
""" Basic form from Swaps model. See inherited models below. """
class Meta:
model = Swaps
class NewSwapForm(SwapForm):
# Using a custom argument 'user'
def __init__(self, user, *args, **kwargs):
super(NewSwapForm, self).__init__(*args, **kwargs)
self.rank = User.objects.get(id=user.id).firefighter_rank_set.get().rank
provider = UserModelChoiceField(User.objects.all().
order_by('last_name').
filter(firefighter__hirestatus='active')
### .filter(firefighter_rank__rank=self.rank) ###
)
class Meta(SwapForm.Meta):
model = Swaps
fields = ['provider', 'date_swapped', 'swap_shift']
Thanks!
You can't do it that way, because self doesn't exist at that point - and even if you could, that would be executed at define time, so the rank would be static for all instantiations of the form.
Instead, do it in __init__:
provider = UserModelChoiceField(User.objects.none())
def __init__(self, user, *args, **kwargs):
super(NewSwapForm, self).__init__(*args, **kwargs)
rank = User.objects.get(id=user.id).firefighter_rank_set.get().rank # ??
self.fields['provider'].queryset = User.objects.order_by('last_name').filter(
firefighter__hirestatus='active', firefighter_rank__rank=rank)
I've put a question mark next to the rank line, because rank_set.get() isn't valid... not sure what you meant there.

How can I send variables to my forms on init in Django?

I want to send variables to a form in my Django project so that my form's save method associates the correct object with the foreign key in the model.
I tried setting it in the init method, but that doesn't seem to work.
Here is my Form's init:
def __init__(self, rsvp, max_guests=2, *args, **kwargs):
super(RSVPForm, self).__init__(*args, **kwargs)
self.rsvp = rsvp
self.max_guests = rsvp.max_guests
I think you may be looking for initial:
Use initial to declare the initial value of form fields at runtime. For example, you might want to fill in a username field with the username of the current session.
Pulling directly from the docs:
class CommentForm(forms.Form):
name = forms.CharField(initial='class')
url = forms.URLField()
comment = forms.CharField()
f = CommentForm(initial={'name': 'instance'})
This would yield a form with the initial value of name being 'instance'.