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.
Related
MODELS.PY
class Campaign(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
campaign_image = models.ImageField(default="profilepic.jpg",upload_to="campaign_pictures")
FORMS.PY
class RaiseFundsFrom3(forms.ModelForm):
class Meta:
model = Campaign
fields = ['campaign_image']
VIEWS.PY
#login_required
def raise_funds_medical_3(request):
if request.method == 'POST':
form = RaiseFundsFrom3(request.POST, request.FILES or None, instance=request.user)
if form.is_valid():
check = form.save(commit=False)
check.save()
return HttpResponse('form worked')
else:
form = RaiseFundsFrom3()
return render(request,'funds/raise_funds_medical_3.html',{'form':form})
URLS.PY
path('raise/medical/photo', views.raise_funds_medical_3, name="raise_funds_medical_3"),
raise_funds_medical_3.html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group pt-2">
<small>Photo formats must be PNG / JPG / JPEG</small>
<input type="file" name="campaign_image" />
</div>
<button class="btn btn-lg button_bfg_blue" type="submit"> <small><b> NEXT </b></small> </button>
</form>
on form submit, i do not get any error, but image is not uploaded to the required folder.
however, in the raise_funds_medical_3 function within views.py, if i remove instance=request.user, the image gets uploaded but i get following error : NOT NULL constraint failed: funds_campaign.user_id
Your form is a ModelForm for a Campaign, so its instance needs to be a Campaign. Don't assign request.user as its instance!
Now, your form isn't including the user field which is required to save a Campaign, so you should assign that yourself in the view before saving to the database:
campaign = form.save(commit=False) # this gives your the form's instance
campaign.user = request.user # this assigns the user
campaign.save() # this commits to the database
Also you should handle the case where the form isn't valid. This is quite simple, just un-indent the last return in your view function, so that return render(...) is also called in case the form isn't valid.
Finally, instead of returning a response when the form is valid, it's good practice to redirect to another view. This way, when the user refreshes the page, the form isn't submitted again. Your final code should look like this:
#login_required
def raise_funds_medical_3(request):
if request.method == 'POST':
form = RaiseFundsFrom3(request.POST, request.FILES or None)
if form.is_valid():
check = form.save(commit=False)
check.user = request.user
check.save()
return redirect(<url_pattern>)
else:
form = RaiseFundsFrom3()
return render(request,'funds/raise_funds_medical_3.html',{'form':form})
Supplementary answer to dirkgroten's one
I have come to completely hate the conventional structuring of a Django Function-based View. They can be re-factored by inverting the validity test and adding one line so that one and only one instantiation of a form is present. The result is IMO far easier to read, and easily generalizes for a view displaying two or more forms.
def raise_funds_medical_3(request):
args = [request.POST, request.FILES or None] if request.method == "POST" else []
form = RaiseFundsFrom3(*args)
if request.method != "POST" or not form.is_valid():
# unbound form or form not valid
return render(request,'funds/raise_funds_medical_3.html',{'form':form})
# form is valid so do the processing and redirect
check = form.save(commit=False)
check.user = request.user
check.save()
return redirect(<url_pattern>)
If you want to process >1 form, the test becomes
if request.method != "POST" or any(
[ not form.is_valid(), not form2.is_valid(), ...]):
which forces evaluation of .is_valid() for all forms, even if the first was not valid, so that all the error messages are shown to the user.
In a complex business application, the processing of a successful form submission may be quite a few more lines of code than this simple example. Having it at the end, not indented, isolated from all the boilerplate save the return redirect(...), makes things much easier!
This fails for me. It gives me a "405 Method not allowed error." It is referring to the POST method yes? How does one check for a POST? Or should I do something totally differently?
class StuffList(ListView):
template_name = "list.html"
queryset = Stuff.objects.all().order_by('-whatever')
context_object_name = 'stuff'
def get(self, request, *args, **kwargs):
if request.POST:
q = request.POST.get('q')
stuff = Stuff.objects.filter(user__icontains=stuff)
return render(request, self.template_name, {'stuff': stuff, 'q': q })
In my form, I have the token placed right inside the form action, like so:
<form action="/stuff/" method="post" name="q">
{% csrf_token %}
It's because you're making a POST request but you've only defined a GET method. If you add a POST method, it should do the trick :
def post(self, request, *args, **kwargs):
stuff = request.POST.get('q')
stuff = self.get_queryset().filter(user__icontains=stuff)
return render(request, self.template_name, {'stuff': stuff, 'q': q })
However, if you're only filtering the list, using the get is a correct solution, in this case, you have to change your form tag method="post" to method="get" and make you're GET method something like this:
def get(self, request, *args, **kwargs):
stuff = self.get_queryset()
if request.GET.get('q'):
q = request.GET.get('q')
stuff = stuff.filter(user__icontains=q)
return render(request, self.template_name, {'stuff': stuff, 'q': q })
Maybe you should look at the Django forms which can do data validation, form generation and much more.
https://docs.djangoproject.com/fr/1.8/topics/forms/
The answer given by Francois (https://stackoverflow.com/a/33876878/1126755) is very helpful. I'd like to add this more generic version of the post handler:
def post(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
context = self.get_context_data()
return self.render_to_response(context)
In this way other aspects of the ListView can remain unchanged.
I am using a Django form wizard to enter data into a form page, and then display it in a confirmation page. However, when I try to call self.get_cleaned_data_for_step(step_name), I get a "'MyForm' object has no attribute 'cleaned_data'." I know this can happen if the form fails to validate, so I overrode the is_valid method in my form class to always return True, just as a test, but I still get this error. My relevant code is below:
forms.py
...
class MealForm(forms.Form):
modifications = forms.CharField()
def __init__(self, *args, **kwargs):
menu_items = kwargs.pop('menu_items')
super(MealForm, self).__init__(*args, **kwargs)
for item in menu_items:
self.fields[str(item.name)] = forms.IntegerField(widget=forms.NumberInput(attrs={'value': 0}))
def is_valid(self):
return True
urls.py
...
url(r'^(?P<url>[-\w]+)/meal/$',
login_required(views.MealFormWizard.as_view(views.MealFormWizard.FORMS)), name="meal"),
views.py
...
class MealFormWizard(SessionWizardView):
FORMS = [('meal_form', MealForm),
('meal_form_confirmation', MealFormConfirmation)]
TEMPLATES = {'meal_form': 'restaurant/createMeal.html',
'meal_form_confirmation': 'restaurant/confirmation.html'}
def get_form_kwargs(self, step=None):
kwargs = {}
url = self.kwargs['url']
restaurant = Restaurant.objects.get(url=url)
menu_items = MenuItem.objects.filter(restaurant=restaurant)
if step == 'meal_form':
kwargs['menu_items'] = menu_items
return kwargs
def get_context_data(self, form, **kwargs):
context = super(MealFormWizard, self).get_context_data(form=form, **kwargs)
if self.steps.current == 'meal_form':
context.update({...objects/vars...})
if self.steps.current == 'meal_form_confirmation':
cd = self.get_cleaned_data_for_step('meal_form') **This is where my error occurs**
createMeal.html
...
<form action="" method="post">
{% csrf_token %}
{{ wizard.management_form }}
{{ wizard.form }}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.next }}">Submit Meal</button>
</form>
Upon submitting the form in createMeal.html I should be able to access the cleaned data for the previous step in the get_context_data method of my MealFormWizard class in views.py via a call to self.get_cleaned_data_for_step('meal_form'). However, this is not the case, but I am not sure where I went wrong.
Overriding is_valid like that won't work - if you follow the code you will see that the form's cleaned_data attribute is set by the normal is_valid method.
The docs say that if the form is invalid, then get_cleaned_data_for_step will return None, you need to write your code so it can handle this.
In case this is helpful to anyone. My problem was that in my createMeal.html, my button was simply taking the wizard to the next step, bypassing any validation. The proper solution is to make a simple submit button to submit the form, at which point the wizard will then validate the form, and if it is valid, it will move on to the next step.
I am trying to assign a session variable based on a model or database table on my Django site. In other words, on the first use, I want the user to select a county from a dropdown list and write the name of that county or pk to a session variable. From therek, that data gets pulled into context_processor before hitting the template. Right now, the error I'm getting is
(1062, "Duplicate entry '1' for key 'county_id'")
I'm not sure if it is trying to write multiple entries into the database, but I don't really want anything databased as you would other data. I just want a session variable stored. I am sure my problem is my view, but I can't seem to get it right. In case you couldn't tell, I'm pretty new at this.
I have a model.py defined that accesses another table of counties.
class NonUserCounty(models.Mdel):
county = models.ForeignKey(County)
def __unicode__(self):
return self.county
I have defined a form.py
class NonUserCountyForm(forms.ModelForm):
class Meta:
model = NonUserCounty
And a views.py
def Welcome(request):
if request.method == 'POST':
form = NonUserCountyForm(request.POST)
if form.is_valid():
mycounty = form.cleaned_data['county']
request.session['mycounty'] = mycounty
form.save()
return HttpResponseRedirect('/')
else:
form = NonUserCountyForm()
context = {'form': form}
return render_to_response('welcome.html', context, context_instance=RequestContext(request))
A Context processor
def mynews(request):
now = datetime.now()
if not request.user.is_authenticated():
if not "mycounty" in request.GET:
MyNews = News
county = County
else:
return HttpResponseRedirect('/welcome/')
else:
user = request.user.get_profile()
county = user.county.all()
MyNews = News
And my template....
<form action="" method="post">
{% csrf_token %}
{{ form }}
<p><input type="submit" alt="register" value="Sign Up" /></p>
Why are you calling form.save() if you don't want to save anything in the database? Just remove that line and it should work.
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!