Converting Django DeleteView to work with ajax - django

Im new to django and im stuck with an issue with ajax. I have the code below.
I have a django delte view. I want to use this with ajax. Right now the view is working but the CSRF Token doesnt render.
I want to try and use a class based view as im new to learn. Im not sure why the CSRF TOKEN is not being rendered in the AJAX version. It appears when a browser is used but not when ajax is used.
Can someone please help ?
view.py
class DeleteOrderItemView(SuccessMessageMixin, generic.DeleteView):
model = OrderItem
template_name = 'pos/order_item_confirm_delte.html'
# overwritten the delete to have a conformation code. Pending items do not need a conformation code.
def delete(self, *args, **kwargs):
print("I was called !")
self.object = self.get_object()
conformation_code = self.request.POST.get('cancellation_code')
if conformation_code == str(12345) or self.object.status == "Pending":
# Record code and logged in user
messages.success(self.request, "Order item {} deleted.".format(self.object.item.name))
# print cancelled KOT
return super(DeleteOrderItemView, self).delete(*args, **kwargs)
else:
messages.warning(self.request, "Incorrect conformation code.")
return HttpResponseRedirect(reverse('pos:delete_order_item', kwargs={'pk': self.object.pk}))
def get_success_url(self, **kwargs):
self.object = self.get_object()
order_pk = self.object.order.pk
return reverse_lazy('pos:order_details', kwargs={'pk': order_pk})
def render_to_response(self, context, **response_kwargs):
""" Allow AJAX requests to be handled more gracefully """
if self.request.is_ajax():
context = self.get_context_data(**response_kwargs)
rendered = render_to_string(self.template_name, context)
return JsonResponse({'delete_form': rendered}, safe=False, **response_kwargs)
else:
return super(generic.DeleteView, self).render_to_response(context, **response_kwargs)
Javascript
/* Load the delete form */
$("#js_order_list_items").on("click", "#js_remove_item", function(){
$("#modal-actions").modal("show");
remove_url = $('#js_remove_item').attr('js_delete_url')
$.ajax({
url: remove_url,
type: 'get',
dataType: 'json',
beforeSend: function () {
$("#modal-actions").modal("show");
},
success: function (data) {
console.log(data.delete_form)
$("#modal-actions .modal-content").html(data.delete_form)
}
})
});
HTML
{% load static %}
{% block content %}
<div class="container">
<br/>
<div class="col-12 alert alert-danger">
<h4>Delete item for {{ orderitem.order.customer.name }}</h4>
</div>
<div class="row">
<div class="col-6">
<ul class="list-group">
<li class="list-group-item"><strong>{{ orderitem.item.name }}</strong></li>
<li class="list-group-item">Quantity: {{ orderitem.quantity }}</li>
<li class="list-group-item">Price: {{ orderitem.price }}</li>
<li class="list-group-item">Ordered by: {{ orderitem.order.user.username }}</li>
<li class="list-group-item">
{% if messages %}
<div class="alert alert-danger" role="alert">
{% for message in messages %}
{{ message }}
{% endfor %}
</div>
{% endif %}
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-12">
<hr/>
<form method="POST" action="{% url 'pos:delete_order_item' pk=orderitem.pk %}" class="js_confirm_delete_form">
{% csrf_token %}
{% if orderitem.status == 'Confirmed' %}
<label>Cancellation Code: </label> <input type="password" id="cancellation_code" name="cancellation_code"><br/><hr/>
{% endif %}
<input type="submit" value="Confirm" class="btn btn-danger btn-large btn-lg">
<button class="btn btn-secondary btn-large btn-lg" type="button">Cancel</button>
</form>
</div>
</div>
</div>
{% endblock %}

I found the issue my self. The render_to_string needs the request passed to it for the csrf_token to work.
rendered = render_to_string(self.template_name, context, request=self.request)

Related

Django: Certain users get random 404 error

I'm facing a strange issue that I can't handle on my own.
In normal cases when users click on a link, then they are directed to a page where they can edit their hook baits (objects). However, certain users get 404 errors, but I don't know why because the page is rendered for most users.
html where the link is
<div class="row justify-content-center mx-2" >
<div class="col-12 p-0">
<ul class="list-group text-center custom-borders m-2 p-0">
{% if own_hookbaits.count == 0 %}
<a href="{% url 'user_profile:hookbaits' request.user.fisherman.fisherman_id %}" class="list-group-item" >No hook baits yet</a>
{% else %}
{% for hookbait in own_hookbaits %}
{{ hookbait.name }}
{% endfor %}
{% endif %}
</ul>
</div>
views.py
class HookBaitUpdateView(UpdateView):
model = HookBait
template_name = "user_profile/hookbaits.html"
form_class = HookBaitForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['formset'] = HookBaitFormset(queryset=HookBait.objects.filter(fisherman=self.request.user.fisherman))
return context
def post(self, request, *args, **kwargs):
formset = HookBaitFormset(request.POST)
if formset.is_valid():
return self.form_valid(formset)
else:
return self.form_invalid(formset)
def form_valid(self, formset):
instances = formset.save(commit=False)
for instance in instances:
instance.fisherman = self.request.user.fisherman
instance.save()
return super().form_valid(formset)
def form_invalid(self, formset):
return HttpResponse("Invalid")
def get_success_url(self):
return reverse('user_profile:profile', args=(self.kwargs['pk'],))
urls.py
app_name = "user_profile"
urlpatterns = [
path("profile/<int:pk>/", views.ProfileView.as_view(), name="profile"),
path("profile/<int:pk>/hookbaits/", views.HookBaitUpdateView.as_view(), name="hookbaits"),
]
rendered html
<div class="row justify-content-center m-0">
<div class="col-12 col-md-6 col-lg-4 p-0">
<div class="row mx-3 my-3 justify-content-center text-center">
<div class="card p-2 custom-borders">
<div class="card-body p-2">
<form method="POST">
{% csrf_token %}
<table class="d-flex justify-content-center">
{{ formset.management_form }}
{% for form in formset %}
<tr class="formset_row">
{% for field in form.visible_fields %}
<td class="pb-2">
{% if form.instance.pk %}{{ form.DELETE }}{% endif %}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" class="btn btn-primary w-50 mt-1" style="background-color: #00754B;" value="Mentés">
</form>
</div>
</div>
</div>
</div>
</div>
Any suggestions what the solution would be? Thanks!
there are some possibilities
the wrong link clicked like if your trying access this URL profile/25/hookbaits/
and in the data index there is no HookBait with the id of 25
in HookBaitUpdateView you are trying to get queryset=HookBait.objects.filter(fisherman=self.request.user.fisherman)
maybe there is no hookbait associate with user.fisherman
404 page mostly served when you call get_object_or_404(HookBait, pk=25)
and update view may call this method

Django Forms Invalid but no Errors

Maybe it might be an oversight but I do not know the point where I am getting it wrong.
My form is rendered correctly but it keeps failing without errors.
forms.py
from crispy_forms.helper import FormHelper
from django import forms
from django.utils.translation import ugettext as _
class BeneficiaryForm(forms.Form):
"""Add Beneficiary template form"""
# Form fields
account_currency = forms.ModelChoiceField(queryset=Currency.objects.all(), empty_label=_('Select account currency'))
bank_account_type = forms.CharField(max_length=50, required=False)
email = forms.CharField(max_length=150, required=False, help_text=_("We'll notify them when a transfer is made"))
name = forms.CharField(max_length=50, required=False)
swift_code = forms.CharField(max_length=11, required=False,
widget=forms.TextInput(attrs={'placeholder': 'MSBCCNBJ001'}))
iban = forms.CharField(max_length=34)
def __init__(self, *args, **kwargs):
super(BeneficiaryForm, self).__init__()
self.helper = FormHelper()
self.helper.form_show_labels = False
views.py
def beneficiaries(request):
"""View function for viewing Beneficiaries and adding a Beneficiary instance"""
if request.method == 'POST':
form = BeneficiaryForm(request.POST)
if form.is_valid():
print("Form is valid")
print(request.POST['bank_account_type'])
print(request.POST['email'])
print(request.POST['name'])
print(request.POST['iban'])
print(request.POST['swift_code'])
print("Form is invalid")
print(form.errors)
form = BeneficiaryForm()
context = {
'form': form
}
return render(request, 'dashboard/beneficiaries.html', context)
and in my rendered form. I have this block to show errors and nothing shows up
HTML
<form action="{% url 'beneficiary_index' %}" method="post">
{% csrf_token %}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
{% if field != '__all__' %}
<strong>{{ field.label }}:</strong>
{% endif %}
{{ error|escape}}
</div>
{% endfor %}
{% endfor %}
{% endif %}
<div class="form-group">
<div class="form-label-group">
<label class="form-label" for="default-01">{% trans "Account currency" %}</label>'
</div>
{{ form.account_currency | as_crispy_field }}
</div>
<div class="form-group">
<div class="form-label-group">
<label class="form-label" for="default-01">{% trans "Bank Account type" %}</label>
</div>
{{ form.bank_account_type | as_crispy_field }}
</div>
<div class="form-group">
<div class="form-label-group">
<label class="form-label" for="default-01">Their email(optional)</label>
</div>
{{ form.email | as_crispy_field }}
</div>
<div class="form-group">
<div class="form-label-group">
<label class="form-label" for="default-01">Full name of account holder</label>
</div>
{{ form.name | as_crispy_field }}
</div>
<h6>{% trans "Recipient Bank Information" %}</h6>
<hr>
<div class="form-group">
<div class="form-label-group">
<label class="form-label" for="default-01">Swift code</label>
</div>
{{ form.swift_code | as_crispy_field }}
</div>
<div class="form-group">
<div class="form-label-group">
<label class="form-label" for="default-01">IBAN</label>
</div>
{{ form.iban | as_crispy_field }}
</div>
<div class="form-group">
<button class="btn btn-lg btn-primary btn-block">{% trans 'Add Beneficiary' %}</button>
</div>
</form>
This is the Html to the form. I have just tried all the suggestions and still without the errors.
Form is Invalid is the only thing printed on screen without the form.errors
You need to create the GET request empty form in the else block or return the request in the POST block.
You are re-creating an empty form before returning the response.
def beneficiaries(request):
"""View function for viewing Beneficiaries and adding a Beneficiary instance"""
if request.method == 'POST':
form = BeneficiaryForm(request.POST)
if form.is_valid():
print("Form is valid")
print(request.POST['bank_account_type'])
print(request.POST['email'])
print(request.POST['name'])
print(request.POST['iban'])
print(request.POST['swift_code'])
# do other stuff like saving to DB
# then redirect
else:
print("Form is invalid")
print(form.errors)
elif request.method == 'GET':
form = BeneficiaryForm()
else:
# maybe return 404
pass
context = {
'form': form
}
return render(request, 'dashboard/beneficiaries.html', context)
Or maybe you can try Class Based Views, to avoid the if-else block for GET and POST requests.
Try adding this to the place you want the error to appear (In the HTML file)
Better if you use it inside the tags.
{% if form.errors %}
<p>Form is invalid</p>
{% endif %}
I Hope this answers the question.

Django Nested inline formset-- nested form does not save to DB, no errors thrown

We have a few nested inline formsets in an application. Ideally, the goal is to allow for dynamic and unlimited population of these fields so that users can add an arbitrary number of notes. The form renders, the JS calls are populating; however, I am not seeing the update on the nested from manager.
This is my first Django project and I am not finding anything regarding what is causing the hang up. The Organization is saved in the DB, but the notes are not.
Thanks in advance for any help
Model.py:
class Organization(models.Model):
//irrelevant organization information//
class OrganizationNote(AbstractNotes):
note = models.TextField(blank=True)
org = models.ForeignKey(Organization, on_delete=models.CASCADE,blank=True, null=True)
modelforms.py:
class OrganizationForm(AbstractBigThree):
class Meta:
model = custModels.Organization
fields = '__all__'
orgNoteFormSet = inlineformset_factory(custModels.Organization, custModels.OrganizationNote,
form=OrganizationForm, extra=0)
ModelView.py
class OrganizationCreateView(CreateView, AbstractOrganizationView):
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
org_note_form = orgNoteFormSet()
return self.render_to_response(
self.get_context_data(form=form,
org_note_form=org_note_form))
def get_context_data(self, **kwargs):
data = super(OrganizationCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['notes'] = orgNoteFormSet(self.request.POST)
else:
data['notes'] = orgNoteFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
notes = context['notes']
with transaction.atomic():
self.object = form.save()
if notes.is_valid():
notes.instance = self.object
notes.save()
return super(OrganizationCreateView, self).form_valid(form)
def get_success_url(self):
return '/portal'
template:
{% extends 'base.html' %}
{% load i18n widget_tweaks %}
{% block __file__ %}
<!-- filename == organization_create_form.html -->
{% endblock %}
{% block container %}
<script type="text/javascript">
$(function() {
$(".inline.{{ org_note_form.prefix }}").formset({
prefix: "{{ org_note_form.prefix }}",
})
})
</script>
<div class="content">
<div class="thermometer">
<div style="float:left;padding:10px;">
Dashboard
</div>
<div style="float:left;padding:10px;">
>><a class="back-link" style="padding-left:10px;"href="">Organization List</a>
</div>
</div>
<div class="col-md-7 main">
<h1>Create Organization</h1>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
<div id = "form_set">
<legend>Notes</legend>
</div>
<input type="button" value="Add Note" id="add_more">
<div id="form_set">
{{ org_note_form.management_form }}
{{ org_note_form.non_form_errors}}
{% for form in org_note_form.forms %}
{{form.non_field_errors}}
{{form.errors}}
<table class='no_error'>
{{ form }}
</table>
{% endfor %}
</div>
<div id="empty_form" style="display:none">
<table class='no_error'>
<fieldset>
{{ org_note_form.empty_form}}
<div class="inline {{ org_note_form.prefix }}">
{{ form.note.errors }}
{{ form.note.label_tag }}
{{ form.note }}
</div>
</fieldset>
</table>
</div>
<div>
<input style="margin-top: 30px;" type="submit" class="btn btn-primary" value="Save" />
</div>
</form>
</div>
</div> {% endblock %}
{% block javascripts %}
<script type="text/javascript">
$('#add_more').click(function() {
var form_idx = $('#id_form-TOTAL_FORMS').val();
$('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
});
</script>
{% endblock %}
<script> $('#add_more').click(function() {
var form_idx = $('#id_organizationnote_set-TOTAL_FORMS').val();
$('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_organizationnote_set-TOTAL_FORMS').val(parseInt(form_idx) + 1);
});</script>
the issue was solved by editing the prefix of the model manager. By default django names the prefix as table_set which was missing from above.
add another option to your code:
def form_invalid(self, form):
print(form.errors)
...
to see if you have any validation errors in your form, don't forget to check errors in your inline

login_required decorator make a GET before POST. Then the Post has no 'next' in request

I use this code in login view:
def login():
form = LoginForm()
print("before submit request is ", request)
if form.validate_on_submit():
# Some code was deleted for short
return redirect(request.args.get('next') or url_for('main.index'))
return render_template('accounts/login.html', form=form)
When I click a link which view is :
#login_required
def index(count, page):
pass
I get debug output is :
('before submit request is ', <Request 'http://192.168.1.8:9000/accounts/login?next=%2Fperson' [GET]>)
('before submit request is ', <Request 'http://192.168.1.8:9000/accounts/login' [POST]>)**
So login_required first make GET request with a next field. Then a POST without a next field. So it can't redict to my click.
What can I do?
update add my login template
<div class="row">
<div class="col-md-3">
{{ wtf.quick_form(form, action="/accounts/login") }}
</div>
</div>
I success solve it by change the template:
<div class="row">
<div class="col-md-3">
<form
action="{{ url_for('accounts.login', next=request.args.get('next')) }}"
method="POST"
class="form"
role="form">
{{ form.hidden_tag() }}
{{ wtf.form_errors(form, hiddens='only') }}
{% for field in form %}
{% if not bootstrap_is_hidden_field(field) %}
{{ wtf.form_field(field,
form_type="basic",
horizontal_columns=('lg', 2, 10),
button_map={}) }}
{% endif %}
{% endfor %}
</form>
</div>
</div>
But, I am not sure is this a best answer?

Twitter-Bootstrap modal and django form

I'd like to show last_item in a Twitter-Bootstrap modal after django form submission, however I don't know how to handle the modal. I tried the form button suggested in documentation, but it doesn't process the form data. What do I have to do?
<button data-toggle="modal" data-target="#myModal2">Submit</button>
views.py
def main(request):
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
request.session['name'] = name
mm = MyModel.objects.create(name=name)
mm.save()
return HttpResponseRedirect('/') # Redirect after POST
else:
form = MyModelForm()
args = {}
args['last_item'] = MyModel.objects.all().order_by('pk').reverse()[0]
args['form'] = form
return render(request, 'form.html', args)
form.html
{% extends "base.html" %}
{% block content %}
<form method="POST" id="" action="">
{% csrf_token %}
{{ form.as_p }}
<button>Submit</button>
</form>
<div class="modal" id="myModal2" tabindex="-1" role="dialog"
aria-labelledby="myModal2Label" aria-hidden="true" style="display: none">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModal2Label">Modal header</h3>
</div>
<div class="modal-body">
<p>Last item: {{ last_item }}</p>
</div>
</div>
{% endblock %}
{% block scripts %}
{% endblock %}
It seems like bootstrap calls event.preventDefault() on click, which prevents the form from being submited.
You should bind your own event on this button and close the modal programaticaly.
It could look like:
$('form').submit(function() {
$('#myModal2').modal('hide');
})
I did not test this code but it should be a good start.