I have a Django ModelForm that isn't displaying field errors properly in the template. I have several fields that are required, and what I believe to be the correct logic to capture and display the errors to the user.
When submitting the form, I get the following error:
Traceback (most recent call last):
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/core/handlers/base.py", line 114, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/views/generic/base.py", line 69, in view
return self.dispatch(request, *args, **kwargs)
File "/home/thevariable/webapps/va_jobs/va_jobs/jobs/views.py", line 29, in dispatch
return super(ApplicationCreateView, self).dispatch(*args, **kwargs)
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/views/generic/base.py", line 87, in dispatch
return handler(request, *args, **kwargs)
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/views/generic/edit.py", line 205, in post
return super(BaseCreateView, self).post(request, *args, **kwargs)
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/views/generic/edit.py", line 170, in post
if form.is_valid():
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/forms/forms.py", line 129, in is_valid
return self.is_bound and not bool(self.errors)
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/forms/forms.py", line 121, in errors
self.full_clean()
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/forms/forms.py", line 274, in full_clean
self._clean_form()
File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/forms/forms.py", line 300, in _clean_form
self.cleaned_data = self.clean()
File "/home/thevariable/webapps/va_jobs/va_jobs/jobs/forms.py", line 23, in clean
resume_ext = resume.name.lower().split('.')[1]
AttributeError: 'NoneType' object has no attribute 'name'
Here is my forms.py:
from django.forms import ModelForm
from .models import Application
class ApplicationForm(ModelForm):
class Meta:
model = Application
fields = [
'first_name',
'last_name',
'email_address',
'phone_number',
'salary_requirement',
'resume',
'portfolio_url',
'description',
'can_relocate',
'start_date',
]
def __init__(self, *args, **kwargs):
super(ApplicationForm, self).__init__(*args, **kwargs)
self.fields['first_name'].required = True
self.fields['last_name'].required = True
self.fields['email_address'].required = True
self.fields['phone_number'].required = True
self.fields['salary_requirement'].required = False
self.fields['resume'].required = True
self.fields['portfolio_url'].required = False
self.fields['description'].required = False
self.fields['can_relocate'].required = True
self.fields['start_date'].required = False
def clean(self):
cleaned_data = super(ApplicationForm, self).clean()
resume = cleaned_data.get('resume')
resume_ext = resume.name.lower().split('.')[1]
if not resume_ext in ('pdf', 'doc', 'docx'):
del cleaned_data["resume"]
msg = u"Your file must be a PDF or DOC file type."
raise forms.ValidationError(msg)
#self._errors["resume"] = self.error_class([msg])
return cleaned_data
Here is my view:
class ApplicationCreateView(CreateView):
model = Application
form_class = ApplicationForm
success_url = 'submitted/'
def dispatch(self, *args, **kwargs):
self.job = get_object_or_404(Job, slug=kwargs['slug'])
return super(ApplicationCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
#Get associated job and save
self.object = form.save(commit=False)
self.object.job = self.job
self.object.save()
# Gather cleaned data for email send
first_name = form.cleaned_data.get('first_name')
last_name = form.cleaned_data.get('last_name')
email_address = form.cleaned_data.get('email_address')
phone_number = form.cleaned_data.get('phone_number')
salary_requirement = form.cleaned_data.get('salary_requirement')
description = form.cleaned_data.get('description')
portfolio_url = form.cleaned_data.get('portfolio_url')
can_relocate = form.cleaned_data.get('can_relocate')
start_date = form.cleaned_data.get('start_date')
resume = self.object.resume
job = self.object.job
#Compose message
email = EmailMessage()
email.body = 'Name: ' + first_name + last_name + '\n' + 'Email: ' + email_address + '\n' + 'Phone number: ' + str(phone_number) + '\n' + 'Salary requirement: ' + str(salary_requirement) + '\n' + 'Description: ' + description + '\n' + 'Portfolio URL: ' + portfolio_url + '\n' + 'Can relocate: ' + str(can_relocate) + '\n' + 'Start date: ' + str(start_date)
email.subject = 'A new application has been submitted for %s' % (job)
email.from_email = 'noreply#abc.com'
email.to = ['jobs#abc.com',]
email.bcc = ['abc#abc.com',]
email.attach(resume.name, resume.read())
email.send()
return HttpResponseRedirect(self.get_success_url())
def get_context_data(self, *args, **kwargs):
context_data = super(ApplicationCreateView, self).get_context_data(*args, **kwargs)
context_data.update({'job': self.job})
return context_data
And here is my template (using Django widget tweaks https://pypi.python.org/pypi/django-widget-tweaks):
<p><em>Note: Fields with an asterisk are required.</em></p>
<form role="form" action="" enctype="multipart/form-data" method="post">
{% csrf_token %}
{% for field in form.visible_fields %}
<div class="form-group">
{% if field.errors %}
<ul class="list-unstyled list-inline">
{% for error in field.errors %}
<li class="text-warning"><span class="glyphicon glyphicon-warning-sign"></span> {{ error|escape }}</li>
{% endfor %}
</ul>
{% endif %}
{{ field.label_tag }}
{% if field.name == "portfolio_url" %}
{% if job.portfolio_required %} <small><span class="glyphicon glyphicon-asterisk"></span></small>
{% endif %}
{% endif %}
{% if field.field.required %} <small><span class="glyphicon glyphicon-asterisk"></span></small>{% endif %}
{% if field.name == "resume" or field.name == "can_relocate" %}
{{ field|add_class:"form-control short" }}
{% elif field.name == "start_date" %}
<input id="id_start_date" class="form-control short" name="start_date" type="date" /><input id="initial-id_start_date" name="initial-start_date" type="hidden" />
{% elif field.name == "portfolio_url" %}
{% if job.portfolio_required %}
<input class="form-control" id="id_portfolio_url" maxlength="200" name="portfolio_url" type="url" required />
{% endif %}
{% else %}
{{ field|add_class:"form-control" }}
{% endif %}
{% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div>
{% endfor %}
<input type="submit" class="btn btn-default" value="Apply">
</form>
For the record, my solution was as follows (in forms.py):
def clean(self):
cleaned_data = super(ApplicationForm, self).clean()
if cleaned_data.get('resume'):
resume = cleaned_data.get('resume')
resume_ext = resume.name.lower().split('.')[1]
if not resume_ext in ('pdf', 'doc', 'docx'):
del cleaned_data["resume"]
msg = u"Your file must be a PDF or DOC file type."
self._errors["resume"] = self.error_class([msg])
return cleaned_data
else:
return cleaned_data
This was inspired by a comment on the original question (Django ModelForm not showing field errors).
All simple, in stack-trace says: "Object resume have no attribute name"
resume = cleaned_data.get('resume')
Maybe resume not in cleaned data ?
try to print it, or put pudb there.
Here is Exception, cuz he is None:
resume_ext = resume.name.lower().split('.')[1]
Related
ValueError: invalid literal for int() with base 10: 'slug-1-2'
2nd time i am facing this error again. I am trying to allow users to edit their comments in any blog post but it seems that there is a problem with this line:
comment = get_object_or_404(Comment, id=post_id)
This has worked for other functions (eg when i created a function that can delete comments) but not this one. Any idea how I can resolve this? I got a feeling its sth to do with when I add comments in the url I use slug and <post_id> when I edit comments url. But it worked for my delete function and i did use post_id for that function. Thanks
models.py
class Comment(models.Model):
post = models.ForeignKey(BlogPost, related_name='comments', on_delete=models.CASCADE)
name = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='name', on_delete=models.CASCADE)
body = models.TextField()
class BlogPost(models.Model):
title = models.CharField(max_length=50, null=False, blank=False, unique=True)
body = models.TextField(max_length=5000, null=False, blank=False)
slug = models.SlugField(blank=True, unique=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
views.py
def edit_own_comment(request, post_id):
context = {}
comment = get_object_or_404(Comment, id=post_id)
if request.method == 'POST':
form = UpdateCommentForm(request.POST, instance=comment)
if comment.name == request.user and form.is_valid():
obj = form.save(commit=False)
obj.save()
messages.success(request, 'Your comment has been edited', extra_tags='editedcomment')
return redirect(reverse("HomeFeed:detail", kwargs={'slug': comment.post.slug }))
form = UpdateCommentForm(
initial = {
"body": comment.body,
}
)
context['form'] = form
return render(request, 'HomeFeed/edit_comment.html', context)
class AddCommentView(LoginRequiredMixin, DetailView, FormView):
login_url = 'must_authenticate'
model = BlogPost
form_class = CommentForm
template_name = 'HomeFeed/add_comment.html'
def form_valid(self, form):
comment = form.save(commit=False)
comment.name = self.request.user
comment.post = self.get_object()
comment.save()
return redirect(reverse("HomeFeed:detail", kwargs={'slug': comment.post.slug }))
def delete_any_comment(request, post_id):
if request.method == 'POST':
comment = get_object_or_404(Comment, id=post_id)
if comment.post.author == request.user:
comment.delete()
return redirect(reverse("HomeFeed:detail", kwargs={'slug': comment.post.slug }))
forms.py
class UpdateCommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['body']
def save(self, commit=True):
comment = self.instance
comment.body = self.cleaned_data['body']
if commit:
comment.save()
return comment
urls.py
path('comments/<slug>', AddCommentView.as_view(), name= "add_comment"),
path('editowncomments/<post_id>', edit_own_comment, name= "edit_own_comment"),
path('deleteanycomments/<post_id>', delete_any_comment, name= "deleteanycomments"),
html
<form class="create-form" method="POST" enctype="multipart/form-data">{% csrf_token %}
<div class="form-group">
<label for="id_body">Body</label>
<textarea class="form-control" rows="8" type="text" name="body" id="id_body" required>{{form.initial.body}}</textarea>
</div>
<button class="submit-button btn btn-lg btn-primary btn-block" type="submit">Edit Comment</button>
</form>
TraceBack
Internal Server Error: /HomeFeed/editowncomments/slug
Traceback (most recent call last):
File "lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
return view_func(request, *args, **kwargs)
File "HomeFeed/views.py", line 995, in edit_own_comment
comment = get_object_or_404(Comment, id=post_id)
File "lib/python3.8/site-packages/django/shortcuts.py", line 93, in get_object_or_404
return queryset.get(*args, **kwargs)
File "/lib/python3.8/site-packages/django/db/models/query.py", line 399, in get
clone = self.filter(*args, **kwargs)
File "/lib/python3.8/site-packages/django/db/models/query.py", line 892, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "lib/python3.8/site-packages/django/db/models/query.py", line 910, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1290, in add_q
clause, _ = self._add_q(q_object, self.used_aliases)
File "/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1315, in _add_q
child_clause, needed_inner = self.build_filter(
File "/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1251, in build_filter
condition = self.build_lookup(lookups, col, value)
File "/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1116, in build_lookup
lookup = lookup_class(lhs, rhs)
File "/lib/python3.8/site-packages/django/db/models/lookups.py", line 20, in __init__
self.rhs = self.get_prep_lookup()
File "lib/python3.8/site-packages/django/db/models/lookups.py", line 70, in get_prep_lookup
return self.lhs.output_field.get_prep_value(self.rhs)
File "/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 972, in get_prep_value
return int(value)
ValueError: invalid literal for int() with base 10: 'slug'
SLDEM's suggestion:
#login_required(login_url=reverse_lazy("must_authenticate"))
def edit_own_comment(request, post_id):
context = {}
comment = get_object_or_404(Comment, id='slug')
if comment.name != request.user:
return HttpResponse('You did not write the comment.')
if request.method == 'POST':
form = UpdateCommentForm(request.POST or None, instance=comment)
if comment.name == request.user and form.is_valid():
obj = form.save(commit=False)
#this prints a success message after it is safe
obj.save()
return redirect(reverse("HomeFeed:detail", kwargs={'slug': comment.post.slug }))
form = UpdateCommentForm(
initial = {
"body": comment.body,
}
)
context['form'] = form
return render(request, 'HomeFeed/edit_comment.html', context)
path('editowncomments/<int:post_id>', edit_own_comment, name= "edit_own_comment"),
html
{% if not blog_post.comments.all %}
<p>No comments yet... Add a comment</p>
{% else %}
Add a comment
<br>
{% for comment in blog_post.comments.all %}
<strong>
{{ comment.name}}
{{ comment.date_added }}
</strong>
{{ comment.body }}
{% if comment.name == request.user and blog_post.author != request.user %}
<form action = "{% url 'HomeFeed:deletecomments' comment.id %}" method = "POST"> {% csrf_token %}
<button class="btn btn-sm btn-danger">Delete</button>
</form>
{% endif %}
{% if blog_post.author == request.user %}
<form action = "{% url 'HomeFeed:deleteanycomments' comment.id %}" method = "POST"> {% csrf_token %}
<button class="btn btn-sm btn-danger">Delete</button>
</form>
{% endif %}
{% if comment.name == request.user %}
<a class="btn btn-sm btn-warning col-lg-4" href="{% url 'HomeFeed:edit_own_comment' comment_id %}">Edit comment</a>
{% endif %}
{% endfor %}
{% endif %}
detail views.py
class DetailBlogPostView(BlogPostMixin,DetailView):
template_name = 'HomeFeed/detail_blog.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
blog_post=self.get_object()
blog_post.save()
context['blog_post'] = blog_post
account = Account.objects.all()
context['account'] = account
#aka if blog post does belong to me
if blog_post.author != self.request.user:
if self.request.user.is_authenticated and \
blog_post.interest_set.filter(user__id=self.request.user.id).exists():
submittedinterest = True
context["interest_pk"]=blog_post.interest_set.first().pk
#if blog post belongs to me
else:
submittedinterest = False
context['submittedinterest'] = submittedinterest
if blog_post.interest_set.exists():
context["interest_pk"]=blog_post.interest_set.first().pk
return context
Well looks like the issue here is with your url, make it like this:
path('edit-own-comments/<int:post_id>', edit_own_comment, name="edit_own_comment"),
(Add the specification that the passed variable is actually an integer) And then use the value of your Comment instance id in your view.
Ok so based on your edits:
Where you have your html to edit the comment:
{% if comment.name == request.user %}
<a class="btn btn-sm btn-warning col-lg-4" href="{% url 'HomeFeed:edit_own_comment' comment.id %}">Edit comment</a> <!-- here change comment_id to comment.id to pass the correct value to the url -->
{% endif %}
In your view where you are getting the comment:
comment = get_object_or_404(Comment, id=post_id) # also rename post_id to something like comment_id here and in the url to make it more readable
And it should work.
# models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from phonenumber_field.modelfields import PhoneNumberField
class UserprofileCity(models.manager):
def get_queryset(self):
return super(UserprofileCity,self).get_queryset().filter(city='surat')
class UserProfile(models.Model):
user= models.OneToOneField(User,on_delete=models.CASCADE)
# you an add extra fields as per requirements after
city = models.CharField(max_length=100,default='')
description = models.CharField(max_length= 500,default='')
phone_number = PhoneNumberField(default='+91',max_length=13,help_text='enter mobile number with country code')
age = models.IntegerField(default=18)
image = models.ImageField(upload_to='profile_image',blank=True)
def __str__(self):
return self.user.username
def create_profile(sender,**kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile,sender=User)
# views.py:
from django.shortcuts import render,redirect
from django.urls import reverse
from .forms import RegistrationForm,EditProfileForm,ProfileForm
from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth import update_session_auth_hash
def register(request):
if request.method =='POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
return redirect(reverse('accounts:login'))
else:
form = RegistrationForm()
args = {'form':form}
return render(request,'accounts/reg_form.html',args)
def profile_view(request,pk=None):
if pk:
user = User.objects.get(pk=pk)
else:
user = request.user
args = {'user':user}
return render(request,'accounts/profile.html',args)
def profile_edit(request):
if request.method =='POST':
form = EditProfileForm(request.POST,instance=request.user)
profile_form = ProfileForm(request.Post,request.FILES,instance=request.user.userprofile)
if form.is_valid() and profile_form.is_valid():
user_form = form.save()
custom_form = profile_form.save(False)
custom_form.user = user_form
custom_form.save()
return redirect(reverse('accounts:profile_view'))
else:
form = EditProfileForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.userprofile)
args = {
'form':form, 'profile_form':profile_form
}
return render(request,'accounts/profile_edit.html',args)
def password_change(request):
if request.method=='POST':
form = PasswordChangeForm(data = request.POST,user=request.user)
if form.is_valid():
form.save()
update_session_auth_hash(request,form.user)
return redirect(reverse('accounts:profile_view'))
else:
return redirect(reverse('accounts:change_password'))
else:
form = PasswordChangeForm(user = request.user)
args = {'form':form}
return render(request,'accounts/password_change.html',args)
# urls.py
from django.urls import reverse_lazy
from . import views
from django.urls import path
from django.contrib.auth.views import (
LoginView,
LogoutView,
PasswordResetView,
PasswordResetDoneView,
PasswordResetCompleteView,
PasswordResetConfirmView,
)
app_name = 'accounts'
urlpatterns = [
###
#user
###
path('login/',LoginView.as_view(template_name = 'accounts/login.html'),name='login'),
path('logout/',LogoutView.as_view(template_name='accounts/logout.html'),name = 'logout'),
path('register/',views.register,name='register'),
###
#profile
###
path('profile/',views.profile_view,name='profile_view'),
path('profile/<pk>/',views.profile_view,name='other_profile_view'),
path('profile/edit/',views.profile_edit,name='profile_edit'),
###
#change_password
###
path('change-password/',views.password_change,name='password_change'),
###
# reset password
###
path('reset-password/',PasswordResetView.as_view(
template_name = 'accounts/password_reset.html',
email_template_name = 'accounts/password_reset_email.html',
subject_template_name = 'accounts/password_reset_subject.txt',
success_url = reverse_lazy('accounts:password_reset_done'),
),name='password_reset'),
path('password_reset/done/',PasswordResetDoneView.as_view(
template_name = 'accounts/password_reset_done.html',
),name = 'password_reset_done'),
path('reset/<uidb64>/<token>/',PasswordResetConfirmView.as_view(
success_url = reverse_lazy('accounts:password_reset_complete'),
template_name = 'accounts/password_reset_confirm.html',
),name='password_reset_confirm'),
path('reset/done/',PasswordResetCompleteView.as_view(
template_name = 'accounts/password_reset_complete.html',
),name='password_reset_complete'),
# profile.html
{% extends 'base.html' %}
{% load static %}
{% block title %}
Profile
{% endblock %}
{% block body %}
<div class="jumbotron">
<div class="col-md-8">
<h1>{{user}} Profile</h1>
<hr>
<p> Username : {{user}} </p>
<p> First Name : {{user.first_name}} </p>
<p> Last Name : {{user.last_name}} </p>
<p> Age : {{ user.userprofile.age }} </p>
<p> Email : {{ user.email}} </p>
<p> Description : {{ user.userprofile.description }} </p>
<p> Phone : {{ user.userprofile.phone_number }}</p>
<p> city : {{ user.userprofile.city }} </p>
<p> Website : {{ user.userprofile.website }} </p>
</div>
<div class="col-md-4">
{% if user.userprofile.image %}
<img src="{{ user.userprofile.image.url }}" width="400"><br>
{% endif %}
<br>
<div class="col-md-6">
<a class="btn btn-info" href="{% url 'accounts:profile_edit' %}">Edit Profile</a>
</div>
<div class="col-md-6">
<a class="btn btn-info" href="{% url 'accounts:password_change' %}">Change Password</a>
</div>
</div>
</div>
{% endblock %}
When I run with these files, I get this error:
traceback:
The above exception (invalid literal for int() with base 10: 'edit') was the direct cause of the following exception:
File "D:\python\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
response = get_response(request)
File "D:\python\lib\site-packages\django\core\handlers\base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Kraj\Desktop\practice\demo4\post\accounts\views.py", line 25, in profile_view
user = User.objects.get(pk=pk)
File "D:\python\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "D:\python\lib\site-packages\django\db\models\query.py", line 418, in get
clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
File "D:\python\lib\site-packages\django\db\models\query.py", line 942, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "D:\python\lib\site-packages\django\db\models\query.py", line 962, in _filter_or_exclude
clone._filter_or_exclude_inplace(negate, *args, **kwargs)
File "D:\python\lib\site-packages\django\db\models\query.py", line 969, in _filter_or_exclude_inplace
self._query.add_q(Q(*args, **kwargs))
File "D:\python\lib\site-packages\django\db\models\sql\query.py", line 1358, in add_q
clause, _ = self._add_q(q_object, self.used_aliases)
File "D:\python\lib\site-packages\django\db\models\sql\query.py", line 1377, in _add_q
child_clause, needed_inner = self.build_filter(
File "D:\python\lib\site-packages\django\db\models\sql\query.py", line 1319, in build_filter
condition = self.build_lookup(lookups, col, value)
File "D:\python\lib\site-packages\django\db\models\sql\query.py", line 1165, in build_lookup
lookup = lookup_class(lhs, rhs)
File "D:\python\lib\site-packages\django\db\models\lookups.py", line 24, in __init__
self.rhs = self.get_prep_lookup()
File "D:\python\lib\site-packages\django\db\models\lookups.py", line 74, in get_prep_lookup
return self.lhs.output_field.get_prep_value(self.rhs)
File "D:\python\lib\site-packages\django\db\models\fields\__init__.py", line 1776, in get_prep_value
raise e.__class__(
Exception Type: ValueError at /accounts/profile/edit/
Exception Value: Field 'id' expected a number but got 'edit'.
Screenshot here
There is a typo, POST method should be in caps in the edit profile function. Fixing it over here.
def profile_edit(request):
if request.method =='POST':
form = EditProfileForm(request.POST,instance=request.user)
profile_form = ProfileForm(request.POST,request.FILES,instance=request.user.userprofile)
if form.is_valid() and profile_form.is_valid():
user_form = form.save()
custom_form = profile_form.save(False)
custom_form.user = user_form
custom_form.save()
return redirect(reverse('accounts:profile_view'))
else:
form = EditProfileForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.userprofile)
args = {
'form':form, 'profile_form':profile_form
}
return render(request,'accounts/profile_edit.html',args)
I want to learn python and web-dev and I am trying to make a functional web application. Right now I have problem with django-autocomplete-light.
I am trying to use autocomplete for my django application. I followed the tutorial, but when I try to search It says that The results could not be loaded.
But results are shown in dropdown list.
I have a form where user can add new invoce, I am trying to use django-autocomplete-light for autocompleting all suppliers so user, doesn't need to search for it.
My models:
class Supplier(models.Model):
supp_name = models.CharField(max_length=255)
mat_number = models.CharField(max_length=255)
organ = models.CharField(max_length=255, null=True, blank=True)
street = models.CharField(max_length=255, null=True, blank=True)
email = models.CharField(max_length=255, null=True, blank=True)
def __str__(self):
return self.supp_name.encode("utf-8")
def get_absolute_url(self):
return reverse('supplier-detail', args=[str(self.id)])
def __unicode__(self):
return '%s' % (self.supp_name,)
class Invoice(models.Model):
organization = models.ForeignKey(Group, help_text="organization")
input_peo = models.CharField(max_length=150)
date = models.DateTimeField(auto_now_add=True)
supplier = models.ForeignKey(Supplier, on_delete=models.SET_NULL, null=True, related_name='name')
invoice_number = models.CharField(max_length=100, null=True, blank=True, help_text='Številka preračuna')
price = models.FloatField(null=True, blank=True, help_text='Cena brez DDV')
sum_me = models.IntegerField(null=True, blank=True, help_text='Kolicina')
def __str__(self):
return self.organization.name.encode("utf-8")
def get_absolute_url(self):
return reverse('invoice-detail', args=[str(self.id)])
My views:
class AddInvoice(LoginRequiredMixin, generic.View):
login_url = '/accounts/login/'
redirect_field_name = 'redirect_to'
form_class = InvoiceCreate
template_name = 'invoce/invoce_form.html'
def get(self, request):
form = self.form_class(None)
return render(request, self.template_name, {'form': form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
invoice = form.save(commit=False)
invoice.organization = request.user.groups.first()
invoice.input_peo = self.request.user.get_full_name()
invoice.supplier = form.cleaned_data['supplier']
invoice.invoice_number = form.cleaned_data['invoice_number']
invoice.price = form.cleaned_data['price']
invoice.sum_me = form.cleaned_data['sum_me']
invoice.save()
return HttpResponseRedirect('/')
return render(request, self.template_name, {'form': form})
'''
*
AUTOCOMPLETE
*
'''
class SupplierAutoComplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Supplier.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
urls:
urlpatterns += [
url(
r'^autocomplete/$',
views.SupplierAutoComplete.as_view(model=Supplier),
name='supplier-autocomplete',
),
]
If I search just for autocomplete (/autocomplete/) in browser this is what i get:
my forms.py:
class InvoiceCreate(forms.ModelForm):
class Meta:
model = Invoice
fields = [
'supplier',
'invoice_number',
'price',
'sum_me',
]
widgets = {
'supplier': autocomplete.ModelSelect2(url='supplier-autocomplete')
}
and finally my template for form:
{% extends "base_generic.html" %}
{% block content %}
<div class="jumbotron">
<div class="row" style="margin-top:60px">
<form action="" method="post">
{% csrf_token %}
<!-- Left Inputs -->
<div class="col-xs-6 wow animated slideInLeft" data-wow-delay=".5s">
<!-- Supplier -->
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.supplier.errors }}
<label>Supplier:</label>
<div class="container">
{{ form.supplier }}
</div>
</div>
<!-- invoice_number -->
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.invoice_number.errors }}
<input type="text" name="invoice_number" id="id_invoice_number" placeholder="Invoice Number" class="form"/>
</div>
<!-- price -->
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.price.errors }}
<input type="text" name="price" id="id_price" placeholder="Price" class="form"/>
</div>
<!-- sum_me -->
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.sum_me.errors }}
<input type="number" name="sum_me" id="id_sum_me" placeholder="Sum" class="form"/>
</div>
</div><!-- End Left Inputs -->
<!-- Bottom Submit -->
<div class="relative fullwidth col-xs-12">
<!-- Send Button -->
<button type="submit" class="form-btn semibold">Add</button>
</div><!-- End Bottom Submit -->
<!-- Clear -->
<div class="clear"></div>
</form>
</div>
</div>
{{ form.media }}
{% endblock %}
This is traceback I get when I try to search:
[19/Aug/2017 14:29:54] "GET /invoice/autocomplete/?q=frnej HTTP/1.1" 500 16154
[2017-08-19 14:29:54,630] - Broken pipe from ('127.0.0.1', 55490)
Internal Server Error: /invoice/autocomplete/
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/dal/views.py", line 48, in dispatch
return super(ViewMixin, self).dispatch(request, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/views/generic/base.py", line 88, in dispatch
return handler(request, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/views/generic/list.py", line 160, in get
self.object_list = self.get_queryset()
File "/Users/miha/Desktop/#application/django/einvoice/invoice/views.py", line 255, in get_queryset
qs = qs.filter(name__istartswith=self.q)
File "/usr/local/lib/python2.7/site-packages/django/db/models/query.py", line 784, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/django/db/models/query.py", line 802, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1261, in add_q
clause, _ = self._add_q(q_object, self.used_aliases)
File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1287, in _add_q
allow_joins=allow_joins, split_subq=split_subq,
File "/usr/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1212, in build_filter
raise FieldError('Related Field got invalid lookup: {}'.format(lookups[0]))
FieldError: Related Field got invalid lookup: istartswith
I got an error of "global name region is not defined" in the line of "queryset=Result.objects.filter(region=region)".
The mistake might because I shouldn't validate if the queryset result has corresponding record in Database in "def clean".
So I tried to remove "def clean" and put this validation in def get_queryset (the 2nd views.py). Then if I enter something with no queryset result in dababase, it doesn't report error on the form page.
Could any help to find how could I change the code? Thanks in advance.
views.py
class ResultView(ListView):
context_object_name = 'result_list'
template_name = 'result_list.html'
model = Result
def clean(self):
cleaned_data = super(MyForm, self).clean()
region = form.cleaned_data['region']
country= form.cleaned_data['country']
if not Result.objects.filter(region=region,country=country).exists():
self.add_error(ValidationError('No corresponding data exists'))
return cleaned_data
def get_queryset(self):
form = InputForm(self.request.GET)
queryset=Result.objects.filter(region=region) -//here wrong
return queryset
return Result.objects.all()
def get_context_data(self,**kwargs):
context["sales"] = self.get_queryset().aggregate(Sum('sales'))
views.py - if removing def clean-- the error function doesn't work
class ResultView(ListView):
context_object_name = 'result_list'
template_name = 'result_list.html'
model = Result
def get_queryset(self):
form = InputForm(self.request.GET)
if form.is_valid():
country = form.cleaned_data['country']
region = form.cleaned_data['region']
if country !="" and region !="":
if Result.objects.filter(region=region,country=country).exists():
try:
queryset=Result.objects.filter(region=region,country=country)
except:
self.add_error(ValidationError('No corresponding data exists'))
return queryset
return Result.objects.all()
def get_context_data(self,**kwargs):
context["sales"] = self.get_queryset().aggregate(Sum('sales'))
HTML Snippets
<form method="post">{% csrf_token %}
{% csrf_token %}
{{ formset.management_form }}
{{ formset.errors }}
{{ formset.non_field_errors }}
{{ formset.non_form_errors }}
{{ form.non_field_errors }}
......
<!--region--> --it allows to select none
<div class="field {% if field.errors %} field_error{% endif %}" >
<label> Select Region:
{{ form.region }}
{% for region in form.region.choices %}
<option value="region" name= "region" id="id_region">{{region}} </option>
{% endfor %}
</label>
</div>
I don't add " {{ form.non_field_errors }} " because some of the fields it allows to be blank
traceback
Traceback:
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\core\handlers\base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\base.py" in dispatch
89. return handler(request, *args, **kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\list.py" in get
159. self.object_list = self.get_queryset()
File "C:\Users\user\Desktop\XXX\result\views.py" in get_queryset
61. Result.objects.filter(region=region,country=country).exists()
this is obvious:
form = InputForm(self.request.GET)
queryset=Result.objects.filter(region=region)
in this scope, the region is not defined, of course.
you need to retrieve the region from the form, sanitize it and then do the query with it
form = InputForm(self.request.GET)
if form.is_valid():
queryset=Result.objects.filter(region=form.cleaned_data['region'])
and further down, it should be:
def get_queryset(self):
form = InputForm(self.request.GET)
if form.is_valid():
country = form.cleaned_data['country']
region = form.cleaned_data['region']
if country !="" and region !="": # <--- moved one intend to right
if Result.objects.filter(region=region,country=country).exists():
try:
queryset=Result.objects.filter(region=region,country=country)
except:
self.add_error(ValidationError('No corresponding data exists'))
return queryset
return Result.objects.all()
I got the error "type object 'Input' has no attribute 'company', but in the model.py, it is indeed there, so there must be some other errors that I didn't see. Your any help is really appreciated.
input- models.py
class Input(models.Model):
company=models.CharField(max_length=100,default='Empty')
region=models.CharField(max_length=100)
start_date=models.DateField(auto_now=False, auto_now_add=False)
def __unicode__(self):
return self.company
forms.py
class InputForm(forms.ModelForm):
company=forms.CharField(required=True)
regionlist = forms.ModelChoiceField(queryset=Dupont.objects.values('region').distinct())
start_date=forms.DateField(widget=DateInput(),required=True)
class Meta:
model = Input
fields = ('company', 'region','start_date')
widgets = {
'start_date': forms.DateInput(attrs={'class':'datepicker'}),
'end_date': forms.DateInput(attrs={'class':'datepicker'}),
}
views.py
def input(request):
if request.method == 'POST':
form = InputForm(request.POST or None, request.FILES or None)
if form.is_valid():
print 'is valid'
company = form.cleaned_data['company']
region = form.cleaned_data['region']
start_date= form.cleaned_data['start_date']
form.save()
return redirect('result')
else:
return render_to_response('input.html',{'form': form},context_instance=RequestContext(request))
else:
form = InputForm(initial={'company':'coco','uom':'M$'},instance=Input)
return render_to_response('input.html',{'form': form})
html
<form method="post" action="{% url 'result' %}">
{% csrf_token %}
<!--enter company name-->
<div class="field">
<p>Company:<input type="text" name="company" value="{{company}}"/>
</div>
<!--select region from drop down list-->
<div class="field" >
<label> Select the Region:
{{ form.regionlist }}
{% for region in form.regionlist.choices %}
<option value="{{ val }}" name= "region" {% ifequal data.val val %}selected {% endifequal %}></option>
{% endfor %}
</label>
</div>
<!--select start date from drop down list-->
<label for="startDate">Start Month:</label>
<input name="start_date" id="start_date" class="date-picker"/>
<!--submit-->
<div class="fieldWrapper">
<p><input type="submit" value="Submit" /></p></div>
</form>
trackback
Traceback:
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\core\handlers\base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\decorators\csrf.py" in wrapped_view
58. return view_func(*args, **kwargs)
File "C:\Users\user\Desktop\SCOR\inputform\views.py" in input
34. form = InputForm(initial={'company':'coco','uom':'M$'},instance=Input)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\forms\models.py" in __init__
320. object_data = model_to_dict(instance, opts.fields, opts.exclude)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\forms\models.py" in model_to_dict
153. data[f.name] = f.value_from_object(instance)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\db\models\fields\__init__.py" in value_from_object
918. return getattr(obj, self.attname)
Exception Type: AttributeError at /input
Exception Value: type object 'Input' has no attribute 'company'
In this line:
form = InputForm(initial={'company':'coco','uom':'M$'},instance=Input)
you're passing the Input class as instance, instead of an Input instance. To pass an instance you'd need to call the class, ie:
form = InputForm(initial={'company':'coco','uom':'M$'},instance=Input())
but that's totally useless here since your ModelForm already knows which model class to instanciate if you don't provide an instance.