Dajax/Dajaxice saving object in ajax.py with parameters - python

I've run through setup and have all examples on dajaxproject.com running fine, but I'm now having problems using what I've learnt in a more complex use case. I'd like to pass several parameters to the ajax function, along with text from a form, and create an object using those pieces of data.
If anybody can help me out, it would be hugely appreciated.
I'm using jquery and jquery.ba-serializeobject.min.js.
Ajax.py:
#dajaxice_register
def save_comment(req, form, user_username, other_username):
dajax = Dajax()
comment_form = CreateCommentForm(deserialize_form(form))
if comment_form.is_valid():
text = comment_form.cleaned_data['text']
user = User.objects.get(username=user_username)
other_user = User.objects.get(username=other_username)
other_profile = Profile.objects.get(user=other_user)
comment = other_profile.comments.objects.create(owner=user, text=text)
comment.save()
return dajax.json()
JS:
<script type="text/javascript">
function add_comment(){
var user = '{{ person.user.username }}';
var other = '{{ other.user.username }}';
var data = $('#comment_form').serialize(true);
Dajaxice.profiles.save_comment(Dajax.process, {'form': data, 'user_username': user, 'other_username': other });
return false;
}
</script>
HTML:
<div><h4>Post Comment:</h4>
<div id="comment_form_errors"></div>
<form action="" id="comment_form" accept-charset="utf-8" method="post">
{% csrf_token %}
{{ commentform.as_p }}
<p><input class="btn profile-comment-submit" id="submit_profile_comment" onclick="add_comment()" type="submit" alt="register" /></p>
<form>
</div>
In Chrome's debug console the only error I get is Dajaxice: something went wrong.
If I've left anything out that might be important, let me know.
Many thanks,

The only thing that stands out to me (and I'm no expert, so who knows...) is in your ajax.py. I think it should be:
#dajaxice_register
def save_comment(req, form, user_username, other_username):
dajax = Dajax()
comment_form = CreateCommentForm(deserialize_form(form))
if comment_form.is_valid():
text = comment_form.cleaned_data['text']
user = User.objects.get(username=user_username)
other_user = User.objects.get(username=other_username)
other_profile = Profile.objects.get(user=other_user)
comment = Comment(owner=user, text=text)
comment.save()
other_profile.comments.add(comment)
# I don't think you need to other_profile.save(), but you can if you want
return dajax.json()

The way you send the form is crusial for Dajax to work. I have succeeded using http://benalman.com/projects/jquery-misc-plugins/#serializeobject and the following javascript:
jQuery('form').submit(function(e) {
e.preventDefault()
var data = jQuery(this).serializeObject();
Dajaxice.app.app_name.function_name(Dajax.process,{'form':data});
return false;
});
It is a little hard to get a complete overview of the problem when I don't see your form. But I would recommend you to create a form CommentForm and populate user and other_user in hidden fields on form initialization. That will make the code less complex
Your save function will then be quite simple:
#dajaxice_register
def function_name(request, form):
dajax = Dajax()
form = CommentForm(form)
if form.is_valid():
form.save()
return dajax.json()

There are a few things that I can see here, but without being able to see the CreateCommentForm() and the model that it is creating a form for some of this may be assumption based. Also assuming there are not any problems with the serialization of the form.
#dajaxice_register
def save_comment(req, form, user_username, other_username):
dajax = Dajax()
user = User.objects.get(username=user_username)
other_user = User.objects.get(username=other_username)
other_profile = Profile.objects.get(user=other_user)
# Required fields of a form must be checked before calling is_valid() or is_valid fails.
comment = Comments(owner=user)
comment_form = CreateCommentForm(deserialize_form(form), instance=comment)
if comment_form.is_valid():
comment_form.save()
dajax.alert('Form is valid')
else:
dajax.alert('Form is invalid')
for error in comment_form.errors:
dajax.add_css_class('#id_%s' % error, 'error')
# Dajax needs something added to it before the dajax.json() can be returned.
return dajax.json()
The form piece can be referred to here: Django using a subset of fields on the form
and the dajax return pieces can be seen in more detail in this dajax example: Dajax form validation example

I've found no way to make it work. I think it is an issue with Dajaxice, what you can do is to avoid the request.POST QueryDict and instead use the request.raw_post_data. You'll need to do the reverse of urlparse:
data = urlparse.parse_qs(request.raw_post_data)
Then you'll need to deserialize it.
data = json.loads(data.get('argv'))
That will return a list of arguments, use the first element in the list.

Related

Django : Form Successful but image not uploaded

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!

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.

Do we need CSRF verification for every POST request?

I am just building a simple HTML form with POST method and unfortunately I am finding CSRF verification error.
This is just a simple html form using POST method on localhost. There are no cross sites involved. I could definitely fix it by using csrf_token but I still don't understand why django is asking me for that..
There are no re-directions/ iframes involved here...
So, why this is happening?? is this normal to all ??
# Also tried using RequestContext(request) but there isn't any change in the error
#settings.py
'django.middleware.csrf.CsrfViewMiddleware' in MIDDLEWARE_CLASSES
#views.py
# url for home page is "" i.e, http://127.0.0.1:8000/
def HomePage (request):
if request.method == "POST":
form = myForm(request.POST)
if form.is_valid():
data = form.cleaned_data
context = { "myForm" : myForm(choices),
"values" : data,
}
return render_to_response("home.html", context)
else:
form = myForm(choices)
context = {"myForm" : form}
return render_to_response("home.html", context)
# home.html
<div id="pingmeeForm">
<form action="" method="post">
<table>
{{myForm.as_table}}
</table>
<input name="enter" type="submit" value="enter"/>
</form>
{{values}}
</div>
# forms.py
class myForm (forms.Form):
def __init__(self, my_choices,*args, **kwargs):
super(myForm, self).__init__(*args, **kwargs)
self.fields['Friends'] = forms.ChoiceField(choices=my_choices)
message = forms.CharField()
If you do a post request, you typically change the state of the server. If you change the state of the server, you don't want to allow other sites to do so. To protect against other sites issueing post-requests on your server, you add csrf protection. Therefore the solution should (imho) never be to remove the Csrf protection. Depending on the situation, either of the following two is the case:
Your post request does not change the state. In that case, make it a get request.
Your post request changes the state. You need CSRF.
The error message you got but didn't show explains exactly what you are doing wrong: you should ensure that
the view function uses RequestContext
for the template, instead of Context.

In Django, how do I make sure that my forms keep its initial value after being submitted but returned to the template due to an error?

Let's say someone submits a form.
It doesn't pass is_valid(), and I return them back to the template.
How do I display the form fields again, without having the person retype their stuff?
I prefer not to use the { initial: } option.
You could implement your view like so:
def showForm(request):
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
# do stuff on success, like redirection
else:
form = MyForm()
return render_to_response(...,
{"form" : form})
# then in the template, use {{ form.as_table }}, for example

POSTing forms in Django's admin interface

I'm writing a Django admin action to mass e-mail contacts. The action is defined as follows:
def email_selected(self,request,queryset):
rep_list = []
for each in queryset:
reps = CorporatePerson.objects.filter(company_id = Company.objects.get(name=each.name))
contact_reps = reps.filter(is_contact=True)
for rep in contact_reps:
rep_list.append(rep)
return email_form(request,queryset,rep_list)
email_form exists as a view and fills a template with this code:
def email_form(request,queryset,rep_list):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('email','noreply#localboast'),['redacted#email.com'],
)
return HttpResponseRedirect('thanks')
else:
form = EmailForm()
return render_to_response('corpware/admin/email-form.html',{'form':form,})
and the template exists as follows:
<body>
<form action="/process_mail/" method="post">
<table>
{{ form.as_table }}
</table>
<input type = "submit" value = "Submit">
</form>
</body>
/process_mail/ is hardlinked to another view in urls.py - which is a problem. I'd really like it so that I don't have to use <form action="/process_mail/" method="post"> but unfortunately I can't seem to POST the user inputs to the view handler without the admin interface for the model being reloaded in it's place (When I hit the submit button with , the administration interface appears, which I don't want.)
Is there a way that I could make the form POST to itself (<form action="" method="post">) so that I can handle inputs received in email_form? Trying to handle inputs with extraneous URLs and unneeded functions bothers me, as I'm hardcoding URLs to work with the code.
You can use django's inbuilt url tag to avoid hardcoding links. see...
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#url
Chances are you'd be better off setting up a mass mailer to be triggered off by a cron job rather than on the post.
Check out the answer I posted here
Django scheduled jobs
Also if you insist on triggering the email_send function on a view update perhaps look at
http://docs.djangoproject.com/en/dev/topics/signals/

Categories