Hidden Field Flow Using Django - python

I'm trying to receive a POST then generate a form with new fields and pass along the values I received in the previous POST as hidden variables. I've done a lot of searching in documentation and can't seem to find anything that connects the two sides of this flow. I'm using Django 1.4 w/ Python 2.7.
views.py
from django.shortcuts import render_to_response
from gateway_interface.forms import newForm
def requestNewForm(request):
if (request.method == "POST"):
form = newForm(request)
return render_to_response('myTemplate.html', {'form' : form})
forms.py
from django import forms
class newForm(forms.Form):
def __init__(self, request):
my_passed_variable = request.POST['pass_variable']
a_new_variable = forms.CharField(max_length = 25)
my_passed_variable = forms.CharField(widget = forms.HiddenInput())
myTemplate.html
<form action="/myNextDjangoView/" method="post">
<div class="fieldWrapper">
I need this value: {{ form.a_new_variable }} <br>
</div>
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
<input type="submit" value="Submit">
</form>
I must be doing something fundamentally wrong. If I use the for loop in the template none of the visible fields show on the page. Nothing I've tried has caused the hidden fields to populate.
Any suggestions? Perhaps I'm missing an import somewhere? Is there something I need to import in forms.py to allow for the use of HiddenInput()?
EDIT 1:
I've modified forms.py to look like this:
form django import forms
class newForm(forms.Form):
a_new_variable = forms.CharField(max_length = 25)
my_passed_variable = forms.CharField(widget = forms.HiddenInput())
def __init__(self, *args, **kwargs):
super(newForm, self).__init__(*args, **kwargs)
This has not changed my output. I still get the same form with no fields showing (hidden or visible). What I need to do is instantiate with an initialization dictionary. (I think?) Where the dictionary contains the name and values for all the hidden fields.
initial_dict = { 'my_passed_variable' : request.POST.get('pass_variable') }
form = newForm(initial = initial_dict)
EDIT 2:
Using the initialization dictionary was a step in the right direction! I am now able to see the visible fields in my form but the hidden fields are still not populating.
views.py
from django.shortcuts import render_to_response
from gateway_interface.forms import newForm
def requestNewForm(request):
if (request.method == "POST"):
initial_dict = { 'my_passed_variable' : request.POST.get('pass_variable') }
form = newForm(initial = initial_dict)
return render_to_response('myTemplate.html', {'form' : form})
EDIT 3:
I've got it working. Thanks to Jordan Reiter for pushing me in the right direction. It turns out the problem was almost entirely the caching of my browser after EDIT 1 above. I moved to Chrome's incognito mode and everything just worked.

There is a definite problem with this code:
from django import forms
class newForm(forms.Form):
def __init__(self, request):
my_passed_variable = request.POST['pass_variable']
a_new_variable = forms.CharField(max_length = 25)
my_passed_variable = forms.CharField(widget = forms.HiddenInput())
First, it's incredibly confusing for there to be two variables with identical names (although one of them is self.my_passed_variable available throughout the form and the other is just my_passed_variable available in __init__ only). I can't help but think you're trying to tie the two variables together somehow, but you're not. Worst/best case scenario (if you rewrote my_passed_variable = request.POST['pass_variable'] as self.my_passed_variable = request.POST['pass_variable']) you're overwriting the value for form field object with a string.
Second, I'm assuming you snipped out a bunch of code from the __init__ function. You're missing the super which actually makes this a form. As it stands, the form object is not going to be instantiated correctly.
If you're trying to do what I think you're trying to do, you want to rewrite it this way:
from django import forms
class newForm(forms.Form):
# first, I'm going to put the fields at the top, I think that's more standard
a_new_variable = forms.CharField(max_length = 25)
my_passed_variable = forms.CharField(widget = forms.HiddenInput())
def __init__(self, request, *args, **kwargs):
super(newForm, self).__init__(*args, **kwargs)
self.fields['my_passed_variable'].initial = request.POST.get('pass_variable') # don't assume the variable is present!

Related

Django-taggit tag value retrieval and formatting failing

I am trying to implement a tagging process for profiles so you can add your hobbies for example.
I have chosen django-taggit as it seemed quite simple and does what I need it to, plus don't really know how to do it myself from scratch.
I have managed to make it work to some extent but I am having issues with 3 things:
Not really sure what's the best way to control the form field for these tags as I generate the form automatically with widget adjustments in meta function of the form, but it might work fine after resolving the below two issues.
When there is no data for the field hobbies (tags) the field gets populated with a single tag of value "[]" as per below image.
When I add a tag of "music" and submit the form after I reload the page I get this "[]" as per image. I assumed this will be dealt with by the library, but I cannot see another similar scenario online.
When I try adding another tag of "games" and save and reload, the below happens. The initial value gets wrapped again.
My model is:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
hobbies = TaggableManager()
My form is:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['hobbies',]
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args,**kwargs)
self.fields['hobbies'].widget = forms.TextInput()
self.fields['hobbies'].widget.attrs['data-role'] = "tagsinput"
self.fields['hobbies'].widget.attrs['class'] = "form-control"
self.fields['hobbies'].required = False
My view function is:
if request.method == 'POST':
user_profile = UserProfile.objects.get(user=request.user)
form = UserProfileForm(request.POST, instance=user_profile)
print(form)
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
print("Form valid")
form.save_m2m()
Using:
<script src="/static/js/tagsinput.js"></script>
<link rel="stylesheet" href="{% static 'css/tagsinput.css' %}" />
I had this exact same problem.
One solution is to apply the data-role="tagsinput" AFTER you turn a list of tags into a comma-separated string for the form.
Here is that solution:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, **kwargs):
self.fields['tags'].widget.attrs['value'] = ", ".join(list(self.instance.tags.names()))
self.fields['tags'].widget.attrs['data-role'] = "tagsinput"
Output:
As you can see, there's a problem with quotes appearing around tags that are multi-word. It also causes new tags with quotes to be saved to the database.
If double-quotes didn't appear around multi-word phrases, this would be the most elegant solution. If someone solves this in the future, drop a note!
My template is this:
<div class="m-3 p-3 border">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" type="submit">Save Form</button>
</form>
</div>
I know I can use a template tag to strip the extra quotes from the tag field itself, but then I'd have to go through and create all the form fields manually just to set the tags template tag.
For the time being, my solution is to simply use Javascript and just modify the Meta widgets section of the form.
FINAL ANSWER (for now):
forms.py
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'tags': forms.TextInput(attrs={
"data-role": "tagsinput",
})
}
custom.js - put this script on the page that loads the form.
document.addEventListener("DOMContentLoaded", function(event) {
let tags_input = document.querySelector('#id_tags');
let tags_input_value = tags_input.value;
let new_value = [...tags_input_value.matchAll(/<Tag:\s*([\w\s]+)>/g)].map(([, m]) => m).join(', ')
tags_input.setAttribute('value', new_value);
}
So all we're doing is modifying the front-end presentation, and leaving all the backend internal forms functionality untouched.
So after quite a few (hundreds) of tests, I finally narrowed down where the issue was and tried to go around it with successful result.
It seems the data got amended into tag objects through tagsinput library I was using. Only when the "data-role" was specified as "tagsinput" in the forms.py the data would already come to html side as those objects and be shown incorrectly. So instead I wanted to keep the data clean and only apply data-role='tagsinput' in the end for visual aspect, which I did using:
var hobbiesTags = document.getElementById("id_hobbies");
if(hobbiesTags){
var att = document.createAttribute("data-role");
att.value = "tagsinput";
hobbiesTags.setAttributeNode(att);
};
And that resulted in the below. Maybe there are better ways to do this, I'm not sure, but it's a pretty clean solution. Share your alternatives.

Link 2 fields from a model in django

I am relatively new to Django and I made a Todo list where user can add a task and mark if its completed. I added a form field of priorities which is a radio select widget. Based on the priority the task field will have red, orange or green color.
The radio buttons appear correctly and I cant post a task without giving an input priority. But the priority is always taken as default(high).
I tried a couple of things to change and display the priorities but nothing worked.
I believe something in the views.py is to be modified to make it work but due to my lack of experience I cannot put a finger on it.
Views.py
#require_POST
def addTodo(request):
form = TodoForm(request.POST)
#print(request.POST['text'])
if form.is_valid():
new_todo = Todo(text = request.POST['text'])
new_todo.save()
for item in form:
return redirect('index')
def completeTodo(request, todo_id):
todo = Todo.objects.get(pk=todo_id)
todo.complete = True
todo.save()
return redirect('index')
form.py
from django import forms
prior_choice =[('high','High'),('mod','Mod'),('low','Low')]
class TodoForm(forms.Form):
text = forms.CharField(max_length = 40,
widget = forms.TextInput(
attrs= {'class': 'form-control', 'placeholder': 'Enter todo e.g. Delete junk files', 'aria-label': 'Todo', 'aria-describedby':'add-btn'}))
priority = forms.CharField(widget=forms.RadioSelect(choices=prior_choice))
models.py
from django.db import models
class Todo(models.Model):
text = models.CharField(max_length=40)
complete = models.BooleanField(default = False)
task_priority = models.CharField(max_length=40, default='high')
def __str__(self):
return self.text
index.html
<ul class="list-group t20">
{% for todo in todo_list %}
{% if todo.task_priority == 'high'%}
{{ todo.text}}</li>
{%elif todo.task_priority == 'mod'%}
{{ todo.text}}</li>
{%elif todo.task_priority == 'low'%}
{{ todo.text}}</li>
{%else%}
<div class="todo-completed"> <li class="list-group-item" style="background-color: green;"> {{ todo.text}}</li></div>
{%endif%}
{% endfor %}
</ul>
Heres a screenshot of the output app
Please help me link the radio button to a task in the list and display accordingly.
Thanks in advance.
The problem is in your view. While you are creating your Todo object you are not passing the priority.
new_todo = Todo(text = request.POST['text'], task_priority = request.POST['priority'])
The code above solves your problem. But I DO NOT RECOMMEND it. You are not leveraging the Django forms. Please use Django forms.cleaned_data to get parameters instead of request.POST or use ModelForm which will allow you to save from form instance directly.
Model Change Advice
However this is not how i would like solve the issue. You can change your model as following to have more djangoic way of doing it:
from django.utils.translation import ugettext_lazy as _
class Todo(models.Model):
PRIORITY_NONE = 0
PRIORITY_LOW = 1
PRIORITY_MODERATE = 2
PRIORITY_HIGH = 3
PRIORITIES = (
(PRIORITY_NONE, _('')),
(PRIORITY_LOW, _('Low')),
(PRIORITY_MODERATE, _('Moderate')),
(PRIORITY_HIGH, _('High')),
)
...
task_priority = models.PositiveSmallIntegerField(choices=PRIORITIES, default=PRIORITY_NONE)
You may need to change your form with the choices Todo.PRIORITIES. Also you may want to use ModelForm which will make things much easier for you.

Set default value for hidden ModelForm field Django

I want to pass an initial value for HiddenInput form field in Meta class of ModelForm object. If the field is not hidden, e.g. as:
class Meta:
model = SomeModel
fields = ['some_id', 'some_amount',]
and I pass initial values via kwargs to this form constructor, then initial values are set to these fields correctly.
But when I try to hide one field (but I still need it to be set up to initial value from kwargs), as e.g.:
class Meta:
model = SomeModel
widgets = {'ord_id': forms.HiddenInput(),}
fields = ['some_id', 'some_amount',]
Then 'ord_id' is not set up to initial value from kwargs and I get the following error when trying to submit such form:
(Hidden field ord_id) Select a valid choice. That choice is not one of the available choices
So is there any way to pass an initial value to the Hidden form field correctly?
You can set defaults value to form fields in two ways.
first method is by passing default values while on initializing the form in your view.py i.e
from forms import ExampleForm
INITIAL_DATA = {'ord_id': 'some_id'}
def my_view(request):
...
if request.method == 'GET':
form = ExampleForm(initial=INITIAL_DATA)
if request.method == 'POST':
form = ExampleForm(request.POST)
...
Second is by overriding the form __init__ method i.e
class ExampleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
"""If no initial data, provide some defaults."""
initial = kwargs.get('initial', {})
initial['ord_id'] = 'ord_id'
kwargs['initial'] = initial
super(ExampleForm, self).__init__(*args, **kwargs)
If you've verified with the inspection tool that your select element really does have the correct option selected, the problem may be that your field is disabled somehow instead of just hidden. I've seen similar error messages from attempting to submit disabled fields. If that's not the case, I may have an alternative solution for designating your fields as hidden.
If you are using django templates you can use widget tweaks to modify form fields in the template, which allows you to do things like add classes and styles or change values. So you can pass the initial arguments to your form as you have already and hide them using template tags. Hope this works for you!
{% for field in form %}
{% if field == form.hidden_field_name %}
{% render_field field hidden='True' id='hidden-field' class='hidden-field-class' %}
{% endif %}
{% endfor %}

getting html form data into django class based view

I have created a Class view in views.py of the django application.
class HelloTemplate(TemplateView):
template_name = "index.html"
def get_context_data(self, **kwargs):
context = super(HelloTemplate, self).get_context_data(**kwargs)
return context
Now I have a form defined in the html page:
<form method="get">
<input type="text" name="q">
<input type="text" name="q1">
<input type="submit" value="Search">
</form>
As you can see, I am submitting the form on the same page.
Now I want to get the form submitted values in my HelloTemplate class. I don't want to create another class or methods outside the existing class.
Also, I would like to send an error message to the html form if data is not validated in the django.
I don't know how to do this, please help me out.
You need to define get (because your form defined with get method <form method="get">) method in view class:
class HelloTemplate(TemplateView):
template_name = "index.html"
def get_context_data(self, **kwargs):
context = super(HelloTemplate, self).get_context_data(**kwargs)
return context
def get(self, request, *args, **kwargs):
q = request.GET.get('q')
error = ''
if not q:
error = "error message"
return render(request, self.template_name, {'error': error})
More information in django docs here Introduction to Class-based views
There's only one value, and it's in request.GET['q'].
Quick response, I can show you what I did a while ago for a review form (for people to create a new review, one of my models):
def review_form_view(request):
c = {}
c.update(csrf(request))
a = Review()
if request.method == 'POST':
review_form = Review_Form(request.POST, instance=a)
if review_form.is_valid():
a = review_form.save()
return HttpResponseRedirect('../dest_form_complete')
pass
else:
review_form = Review_Form(instance=a)
return render_to_response('../review_form.html', {
'review_form': review_form,
}, context_instance=RequestContext(request))
If you have a user model, comment model, etc. you can probably use something similar to this. Very (very) roughly put, the request is the input that the user fills out in the form, 'POST' is the method called that lets the server know you are adding entries to your database, and is_valid() validates the data according to your models.py parameters (can name be NULL? Is age an integer? etc).
Take a look at https://docs.djangoproject.com/en/dev/topics/forms/ as well for more examples and explanation.

Django Forms - How to Use Prefix Parameter

Say I have a form like:
class GeneralForm(forms.Form):
field1 = forms.IntegerField(required=False)
field2 = forms. IntegerField(required=False)
And I want to show it twice on a page within one form tag each time with a different prefix e.g.,:
rest of page ...
<form ..>
GeneralForm(data,prefix="form1").as_table()
GeneralForm(data,prefix="form2").as_table()
<input type="submit" />
</form>
rest of page ...
When the user submits this, how do I get the submitted form back into two separate forms to do validation, and redisplay it?
This was the only documentation I could find and it's peckish.
You process each form as you normally would, ensuring that you create instances which have the same prefixes as those used to generate the form initially.
Here's a slightly awkward example using the form you've given, as I don't know what the exact use case is:
def some_view(request):
if request.method == 'POST':
form1 = GeneralForm(request.POST, prefix='form1')
form2 = GeneralForm(request.POST, prefix='form2')
if all([form1.is_valid(), form2.is_valid()]):
pass # Do stuff with the forms
else:
form1 = GeneralForm(prefix='form1')
form2 = GeneralForm(prefix='form2')
return render_to_response('some_template.html', {
'form1': form1,
'form2': form2,
})
Here's some real-world sample code which demonstrates processing forms using the prefix:
http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/
Even better, I think formsets is exactly what you're looking for.
class GeneralForm(forms.Form):
field1 = forms.IntegerField(required=False)
field2 = forms. IntegerField(required=False)
from django.forms.formsets import formset_factory
# GeneralSet is a formset with 2 occurrences of GeneralForm
# ( as a formset allows the user to add new items, this enforces
# 2 fixed items, no less, no more )
GeneralSet = formset_factory(GeneralForm, extra=2, max_num=2)
# example view
def someview(request):
general_set = GeneralSet(request.POST)
if general_set.is_valid():
for form in general_set.forms:
# do something with data
return render_to_response("template.html", {'form': general_set}, RequestContext(request))
You can even have a formset automatically generated from a model with modelformset_factory , which are used by the automated django admin. FormSet handle even more stuff than simple forms, like adding, removing and sorting items.

Categories