I am trying to implement an ajax view to create an object and then return it and insert it into the template. It is almost working except I cannot seem to get render_to_string() to work to render the html to insert. The object is being created and html is being returned and inserted into the template however the variables are not included in the html. My view is below.
def tr_create_xhr(request, person, slug):
if request.method == "POST":
form = TopicResourceForm(request.POST)
if form.is_valid():
try:
r = Resource.objects.get(url=form.cleaned_data['resource'])
except Resource.DoesNotExist:
r = Resource.objects.create(url=form.cleaned_data['resource'], rtype=form.cleaned_data['rtype'])
r.save()
obj = form.save(commit=False)
obj.resource = r
try:
topic = Topic.objects.get(person__user=request.user, slug__iexact=slug)
except Topic.DoesNotExist:
return Http404
obj.topic = topic
objs = obj.save()
html = render_to_string('includes/tr_inc.html',{"r":objs, "topic":topic})
res = {'html':html}
if request.is_ajax():
return HttpResponse(simplejson.dumps(res), mimetype="application/json")
else:
return HttpResponseRedirect("../..")
return Http404
This is the template "includes/tr_inc.html":
{% load markup %}
{% load people_tags %}
<li>
<h5>{{ r.title }}</h5>
<p><a class="tru" href={{ r.resource.url }}>{{ r.resource.url|sliceit:70 }}</a></p>
<span class="oc"><p>Added {{ r.added }}{% if r.rtype %} |<a href={% url resource_type_detail r.rtype.slug %}>{{ r.rtype }}</a>{% endif %}
| Delete Edit
{{ r.note|markdown }}</span>
</li>
The html string that is returned is the template without any variables inserted.
Model method 'save' doesn't return an object. So you 'objs' variable is empty. You should write
html = render_to_string('includes/tr_inc.html',{"r":obj, "topic":topic})
I had exact the same problem today. It is quite easy. Please check the Django document for this method, there is actually a third optional parameter: context_instance=RequestContext(request). Therefore your render_to_string should be like this:
html = render_to_string(
'includes/tr_inc.html',{"r":obj, "topic":topic},
context_instance=RequestContext(request))
Then everything should work properly.
Related
Package version:1.7.2
Django version:2.1
Python version:3.7
Template pack: Bootstrap4
I have a FileField in my model and I implemented Django's FileExtensionValidator, as well as my own custom field validator to check the file size. It works, but crispy-forms doesn't display error message when these validations fail.
Model
from django.core.validators import FileExtensionValidator
class Project(models.Model):
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='projects',
)
title = models.CharField(
_('project name'),
max_length=100,
help_text=_('Required. 100 characters or fewer.'),
)
slug = models.SlugField(
_('slug'),
max_length=80,
)
created = models.DateTimeField(
_('dateTime created'),
auto_now_add=True,
)
xmlfile = models.FileField(
_('input file'),
upload_to=user_directory_path,
validators=[FileExtensionValidator(allowed_extensions=('xml',))],
help_text=_('Required. Please upload an XML file.'),
)
Form
from django.core.exceptions import ValidationError
def file_size(value):
limit = 9 * 1024 * 1024
if value.size > limit:
raise ValidationError('File too large. Size should not exceed 9 MiB.')
class ProjectForm(forms.ModelForm):
xmlfile = forms.FileField(
label='XML File Upload',
widget=forms.FileInput(attrs={'accept':'application/xml'}),
validators=[file_size],
)
class Meta:
model = Project
widgets = {
'owner': HiddenInput(),
}
Template
{% block content %}
<h1>New Project</h1>
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form|crispy }}
<input type="submit" value="Create" />
</form>
{% endblock content %}
View
class ProjectCreate(CreateView):
form_class = ProjectForm
model = Project
template_name = 'project_new.html'
success_url = reverse_lazy('my_projects_list')
def get_initial(self):
initial = super().get_initial()
initial['owner'] = self.request.user
return initial
When trying to upload an XML file less than 9M, it works and the user is brought to the success URL. But when either file format or file size is wrong, it's correct that we continue to stay on the page of project_new.html, but no error message is displayed on this page related to FileExtensionValidator or file_size().
When I change {{ form|crispy }} to {{ form.as_p }}, the validation error will be displayed on the screen. Do you know how to display validation error messages when using {{ form|crispy }}? Thank you!
According to crispy docs:
'By default when django-crispy-forms encounters errors, it fails silently, logs them and continues working if possible. A settings variable called CRISPY_FAIL_SILENTLY has been added so that you can control this behavior. If you want to raise exceptions instead of logging, telling you what’s going on when you are developing in debug mode, you can set it to:
CRISPY_FAIL_SILENTLY = not DEBUG
Besides you can check other error attributes here(documentation):
https://django-crispy-forms.readthedocs.io/en/d-0/tags.html#helper-attributes-you-can-set
I've got FormView that redirect to a previous page if the form was valid. That's works fine but how can I tell a user that the information has been posted? I want him to see a success message in a modal window after redirect.
I've tried to do it through request.session in get_success_url but it doesn't fit to my goals because a user can submit the form multiple times. So how can I return any message with redirect in get_success_url in FormView?
My FormView
class CatPhotoUploadFormView(FormView):
template_name = 'blank.html'
form_class = CatPhotoForm
def get_success_url(self):
self.request.session['success_message'] = 'Everything is fine'
return reverse('cat:detail_cat', args=(self.kwargs['pk'],))
def form_valid(self, form):
cat = Cat.objects.filter(id__exact=self.kwargs['pk'])
for each in form.cleaned_data['attachments']:
print('****', each, '****', type(each))
Photo.objects.create(photo_path=each, photo_author=self.request.user, photo_cat = cat[0])
return super(CatPhotoUploadFormView, self).form_valid(form)
Use Django messaging framework for this purpose, change get_success_url with message.
from django.contrib import messages
def get_success_url(self):
messages.add_message(self.request, messages.INFO, 'form submission success')
return reverse('cat:detail_cat', args=(self.kwargs['pk'],))
In your template, something like this ( NOTE: remember to pass messages )
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
In fact Django has a ready to use mixin, SuccessMessageMixin, which can be use in class based views to achieve the same purpose.
Like so:
from django.contrib.messages.views import SuccessMessageMixin
class CatPhotoUploadFormView(SuccessMessageMixin, FormView):
template_name = 'blank.html'
form_class = CatPhotoForm
success_message = 'Everything is fine'
...
Very clean and straightforward.
def get_success_url(self):
from django.contrib import messages
messages.success(self.request, 'Yes it works!')
This will do the work
def form_valid(self, form):
print(self.request.POST)
form.save()
messages.add_message(
self.request,
messages.SUCCESS,
'User registered sucessfully'
)
return super().form_valid(form)
I am creating an example to learn more about using inline formsets with SessionWizard. Eventually, I want to integrate dynamic formsets in order to add and delete individual forms via the template before submitting. However, when data is absent from the second form, it fails to validate unlike like a regular ModelForm.
Is there a method within SessionWizard that needs to be overridden? Is it something that is inherently handled within Django?
Guidance and examples would be greatly appreciated.
models.py
class Parent(models.Model):
name = models.CharField(max_length=256)
def __unicode__(self):
return name
class Child(models.Model):
name = models.CharField(max_length=256)
parent = models.ForeignKey(Parent)
def __unicode__(self):
return name
urls.py
test_forms = [['parent', ParentForm],['child', ChildFormSet]]
urlpatterns = patterns('example.views',
url(r'^$', TestWizard.as_view(test_forms)),
)
forms.py
class ParentForm(ModelForm):
class Meta:
model = Parent
class ChildForm(ModelForm):
class Meta:
model = Child
exclude = ('parent',)
ChildFormSet = inlineformset_factory(Parent, Child, extra=1)
class TestWizard(SessionWizardView):
"""
This WizardView is used to create multi-page forms and handles all the
storage and validation stuff using sessions.
"""
#template_name = ''
# def get_template_names(self):
# """
# Returns a list of template names to be used for the request.
# Overridden TemplateResponseMixin for specifying template for step.
# """
# return 'survey/forms/wizard_form.html'
#
# def get_context_data(self, form, **kwargs):
# context = super(TestWizard, self).get_context_data(form=form, **kwargs)
# return context
#
# def get_form_initial(self, step):
# """
# Returns dict (list of key, values) for initial form data.
# Useful for populating form fields with data from prior form, with extra
# logic for dealing with formsets.
# """
# return self.initial_dict.get(step, {})
#
# def get_form(self, step=None, data=None, files=None):
# """
# Constructs the form for a given step - overridden to add extra arguments
# """
# form = super(TestWizard, self).get_form(step, data, files)
# return form
def done(self, form_list, **kwargs):
return render_to_response('survey/thanks.html', {
'form_data': [form.cleaned_data for form in form_list],
})
wizard-form.html
{% extends "base.html" %}
{% load i18n %}
{% block head %}
{{ wizard.form.media }}
{% endblock %}
{% block content %}
<p>DEFAULT WIZARD FORM Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans "first step" %}</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
{% endif %}
<input type="submit" value="{% trans "submit" %}"/>
</form>
{% endblock %}
I am stuck on a simple registration form in django. I get this error when i complete my registration form:
ValueError at /register/
The given username must be set
Request Method:POST
Request URL:http://127.0.0.1:8000/register/
Django Version:1.4
Exception Type:ValueError
Exception Value:The given username must be set
Here is my views.py:
def register_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
email=form.cleaned_data['email']
)
return HttpResponseRedirect('/register/success/')
else:
form = RegistrationForm()
variables = RequestContext(request, {'form': form
})
return render_to_response(
'registration/register.html',
variables
)
and this is my register template:
{% extends "base.html" %}
{% block title %}User Registration{% endblock %}
{% block head %}User Registration{% endblock %}
{% block content %}
<form method="post" action=".">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="register" />
</form>
{% endblock %}
and my url patterns:
urlpatterns = patterns('',
(r'^$', main_page),
(r'^user/(\w+)/$', user_page),
(r'^login/$', 'django.contrib.auth.views.login'),
(r'^logout/$', logout_page),
(r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': site_media}),
(r'^register/$', register_page),
(r'^register/success/$', direct_to_template,
{'template': 'registration/register_success.html'}),
)
I cannot find anything about this issue. I would appreciate some guidance, i am new in Django.
The problem was in my forms.py file:
def clean_username(self):
username = self.cleaned_data['username']
if not re.search(r'^\w+$', username):
raise forms.ValidationError(u'Username can only contain '
'alphanumeric characters and the underscore.')
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(u'Username is already taken.')
After going through the code and indending correctly the problem is fixed. As i said i am new in Django and python
thanks
I have a 4 column table of products in template,each item in table has an anchor with onclick like this:
<div id="tablepro">
<table>
<tr>
{% for product in cat.products.all %}
{% if forloop.counter|divisibleby:4 %}
</tr>
<tr>
<td><center>delete</br><img style="width:200px;height:200px;" class="magnify" src="{{product.image.url}}" /></center></td>
{% else %}
<td><center>delete</br><img style="width:200px;height:200px;" class="magnify" src="{{product.image.url}}" /></center></td>
{% endif %}
{% endfor %}
</table>
</div>
in remove function I have :
function remove(id)
{
var URL='{% url CompanyHub.views.catDetails company.webSite,cat.id %}';
URL+='delpro/'+id+'/';
$.ajax({
url:URL,
type:'POST',
complete:function(){
var str='<table><tr>';
{% for product in cat.products.all %}
{% if forloop.counter|divisibleby:4 %}
str+='</tr><tr>';
str+='<td><center>delete</br><img style="width:200px;height:200px;" class="magnify" src="{{product.image.url}}" /></center></td>';
{% else %}
str+='<td><center>delete</br><img style="width:200px;height:200px;" class="magnify" src="{{product.image.url}}" /></center></td>';
{% endif %}
{% endfor %}
str+='</table>';
$('#tablepro').html(str);
},
error:function(){
alert('Error');
}
});
}
in views.py :
def deleteProduct(request,key,cat_id,pro_id):
try:
company=Company.objects.get(webSite__iexact=key)
except Company.DoesNotExist:
Http404
cat=Category.objects.get(pk=cat_id)
if pro_id:
try:
product=Product.objects.get(pk=pro_id)
product.delete()
except Product.DoesNotExist:
Http404
return render_to_response('CompanyHub/Company/%s/cat_details.html'%(company.theme),{'company':company,'cat':cat}, context_instance=RequestContext(request))
as U see I've returned cat object that now a product object has removed from its list,but I can't get my Div updated in template!that sounds like cat object has not been updated in template tag.
any suggestion is appreciated
Template are compiled on server side and the browser renders the HTML.
To update your div after ajax call, you'd need to update it using javascript code in complete method. Your server side view can return JSON, XML, HTML or any other data type and your complete method has to render that data. Here is an example of how your complete method should look like if your server side returns partial html (i.e. just the table):
complete:function(data) {
$('#tablepro').html(data);
},
Remember that templates are compiled on the server side and the resulting HTML is then passed to the client. This means that the template code you have within your complete function (in the ajax call) will always be the same - in other words, every time you delete an element (and remove is called), you are redisplaying all the original categories again, as the HTML generated within the for loop is created on the server side once - not asynchronously