Django Form Field Validation failed to work - python

Here is my form.
class POICreateForm(ModelForm):
def __init__(self, *args, **kwargs):
super(POICreateForm, self).__init__(*args, **kwargs)
self.fields['latitude'].widget = HiddenInput()
self.fields['longitude'].widget = HiddenInput()
self.fields['picture'].required = True
class Meta:
fields = ['name', 'vip', 'category', 'place', 'latitude', 'longitude', 'picture', 'website', 'description',
'phone',
'email']
model = PointOfInterest
def clean(self):
cleaned_data = super(POICreateForm, self).clean()
pic = cleaned_data.get('picture', None)
if not pic:
msg = 'You must Choose an image'
self.add_error('picture', msg)
return cleaned_data
I have given required=True for picture field and also I overridded the clean method. BUt both failed to show the validation error.
Here is my view
class POIFormCreateView(LoginRequiredMixin, CreateView):
login_url = '/login/'
model = PointOfInterest
form_class = POICreateForm
success_url = reverse_lazy('POIs')
def post(self, request, *args, **kwargs):
query_dict = request.POST.dict()
super(POIFormCreateView, self).post(request, *args, **kwargs)
filtered_dict = {i: j for i, j in query_dict.items() if i.endswith('day')}
out = PointOfInterestQuery.create_or_update_open_hours(filtered_dict)
if out:
return HttpResponseRedirect(reverse('POIs'))
return HttpResponse('Error :(')
template.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<script type="text/javascript">
</script>
<h1>New Point Of Interest</h1>
<form role="form" method="POST" action="/poi/add/" class="post-form form-horizontal"
enctype="multipart/form-data" name="poi_create" onsubmit="return validateForm()">{% csrf_token %}
<div class="container row">
<div class="col-lg-6">
<!-- customizing form -->
{{ form|crispy }}
<!-- End of customization -->
</div>
<div class="col-lg-6">
<div id="map"></div>
{% include 'open_hours.html' %}
</div>
</div>
<button type="submit" id="poi-submit" class="save btn btn-default btn-primary center-block">Save
</button>
</form>
<div class="modal-footer"><a href="/poi/">
<button type="button" class="btn btn-default btn-danger center-block">Cancel
</button>
</a></div>
{% endblock %}

You've overridden the post method and avoided using the form at all. All of this logic should be in the form_valid method instead; that allows the CreateView to do the normal job of form validation and re-displaying the page with errors if the form is invalid, while your processing happens if the form is valid.
Note also however that you should be getting your values to populate filtered_dict from the form's cleaned_data dict, not directly from request.POST.
class POIFormCreateView(LoginRequiredMixin, CreateView):
login_url = '/login/'
model = PointOfInterest
form_class = POICreateForm
success_url = reverse_lazy('POIs')
def form_valid(self,form):
super(POIFormCreateView, self).form_valid(form)
filtered_dict = {i: j for i, j in self.request.POST.items() if i.endswith('day')}
out = PointOfInterestQuery.create_or_update_open_hours(filtered_dict)
if out:
return HttpResponseRedirect(reverse('POIs'))
return HttpResponse('Error :(')

Related

How to make Django form field blank after submit?

I've created a django subscription form. I want to show a 'Thank You' message below the form after submitting but if I submit the form, email field is still showing the value. I've tried HttpResponseRedirect('/') but doing so doesn't show 'Thank You' message.
#Views.py
global categories
categories = ['Development','Technology','Science','Lifestyle','Other']
class IndexView(TemplateView):
model = Post
template_name = 'blog/index.html'
def post(self,request,*args,**kwargs):
context = self.get_context_data()
if request.method == 'POST':
form = SubscriberForm(request.POST)
if context["form"].is_valid():
context["email"] = request.POST.get('email')
form.save()
form = SubscriberForm()
return render(request, 'blog/index.html', context=context)
else:
form = SubscriberForm()
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(IndexView,self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['post_list'] = Post.objects.all()
context["categories"] = categories
form = SubscriberForm(self.request.POST or None) # instance= None
context["form"] = form
return context
#sidebar.html
<div class="subscribe">
<form class="" action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button id="subscribe-button" type="submit" name="button"><i class="fa fa-paper-plane" aria-hidden="true"></i></button>
</form>
{% if email %}
<h6>Thank you for Subscribing!</h6>
{% endif %}
<!-- <i class="fa fa-paper-plane" aria-hidden="true"></i> -->
</div>
#models.py
class Subscribe(models.Model):
email = models.EmailField()
subscribed_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-subscribed_on',)
def __str__(self):
return 'Subscribed by {} on {}'.format(self.email, self.subscribed_on)
#forms.py
class Subscribe(models.Model):
email = models.EmailField()
subscribed_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-subscribed_on',)
def __str__(self):
return 'Subscribed by {} on {}'.format(self.email, self.subscribed_on)
Instead of rendering the template you can redirect() after saving the form.
And to render the success message you can use the django messaging framework.
def post(self,request,*args,**kwargs):
context = self.get_context_data()
if request.method == 'POST':
form = SubscriberForm(request.POST)
if context["form"].is_valid():
context["email"] = request.POST.get('email')
form.save()
messages.success(request, 'Thank you for subscribing')
return redirect('/')
else:
form = SubscriberForm()
return render(request, 'blog/index.html', context=context)
Now in the template you can render the messages like this.
<div class="subscribe">
<form class="" action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button id="subscribe-button" type="submit" name="button"><i class="fa fa-paper-plane" aria-hidden="true"></i></button>
</form>
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<!-- <i class="fa fa-paper-plane" aria-hidden="true"></i> -->
</div>

Filter queryset inside a form

I have one app that holds a list of work orders, and another app that holds list of parts.
class Order(models.Model):
parts = models.ManyToManyField(Part, blank=True) # Assosiated parts
class Part(models.Model):
partnum = models.CharField(max_length=20) # Part number
mwos = models.ManyToManyField('mtn.Order', blank=True) # Assosiated work orders
Now i want to add a button to my DetailView for order which will open a list of parts, which i will be able to add to my order. At the moment i have a created an UpdateView for my order
class AddPartView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Order
form_class = AddPartForm
...
and a form
class AddPartForm(forms.ModelForm):
class Meta:
model = Order
fields = ['parts', ]
labels = {'parts': "Parts", }
def FilterList(request):
qs = Part.objects.all()
search_part_query = request.GET.get('search_part')
if is_valid_queryparam(search_part_query):
qs = qs.filter(Q(partnum__icontains=search_part_query)
| Q(descr__icontains=search_part_query)
).distinct()
return qs
def __init__(self, *args, **kwargs):
super(AddPartForm, self).__init__(*args, **kwargs)
self.fields["parts"].widget = CheckboxSelectMultiple()
self.fields["parts"].queryset = self.FilterList()
for this template
{% block content %}
<form method="GET" action=".">
<div class="form-row justify-content-start">
<div class="form-group col-md align-self-center">
<div class="input-group">
<input class="form-conrol py-2 border-right-0 border" type="search" placeholder="Find part" name="search_part">
<span class="input-group-append">
<div class="input-group-text bg-transparent">
<i class="fa fa-search"></i>
</div>
</span>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary btn-sm btn-block">Search</button>
</form>
<form action="{% url 'mtn:add_part' order.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save</button>
</form>
{% endblock content %}
But when i'm executing it i get
'AddPartForm' object has no attribute 'GET'
error.
I am new to programming, so maybe i am approaching this the wrong way.
A form is normally not aware of the request object. You can make such a form, for example with:
class AddPartForm(forms.ModelForm):
class Meta:
model = Order
fields = ['parts', ]
labels = {'parts': "Parts", }
widgets = {
'parts': CheckboxSelectMultiple
}
def filter_list(self, request):
qs = Part.objects.all()
search_part_query = request.GET.get('search_part')
if is_valid_queryparam(search_part_query):
qs = qs.filter(Q(partnum__icontains=search_part_query)
| Q(descr__icontains=search_part_query)
).distinct()
return qs
def __init__(self, *args, request=None, **kwargs):
super(AddPartForm, self).__init__(*args, **kwargs)
self.fields["parts"].queryset = self.filter_list(request)
In the AddPartView, you can the pass the request as parameter to the form by overriding the .get_form_kwargs(..) method [Django-doc]
class AddPartView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Order
form_class = AddPartForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update(request=self.request)
return kwargs

Page not redirecting after form submit

I have a form which creates a new category. Previously, the form was in a different template which worked fine but since it's only a simple form I have decided to render it in modal form instead of redirecting to a different page.
The user can add a new category, however the success message and the page rendering after the form submit is not shown. It only shows up if you refresh the page. The response message is 302.
I've done similar method with other forms which worked perfectly fine.
forms.py
class CategoryModelForm(forms.ModelForm):
def clean_name(self):
print(self.cleaned_data['name'])
name = self.cleaned_data['name']
try:
Category.objects.get(name__iexact=name)
except ObjectDoesNotExist:
return name
raise forms.ValidationError('Category Name already exists.')
class Meta:
model = Category
fields = ['name']
views.py
#method_decorator(login_required, name='dispatch')
class CategoryView(TemplateView):
template_name = 'content/category_list.html'
def get_context_data(self, **kwargs):
context = super(CategoryView, self).get_context_data(**kwargs)
categories = Category.objects.all()
user = self.request.user
category_list = []
for category in categories:
article_count = category.article_count(user)
include = category.show or user.usertype_is_staff() or user.is_superuser
requested_by = category.requested_by if category.requested_by else ''
cat = {
'reference': category.pk,
'name': category.name,
'show': category.show,
'article_count': article_count,
'has_articles': article_count > 0,
'requested_by': requested_by,
'requested_by_name': requested_by.profile.full_name if requested_by and requested_by.profile.full_name
else '-'
}
include and category_list.append(cat)
context['categories'] = category_list
context['form'] = CategoryModelForm(self.request.POST or None)
return context
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
if context['form'].is_valid():
context['form'].save()
messages.success(request, 'Successfully created new category.')
return redirect('content:category')
return super(CategoryView, self).render_to_response(context)
category_list.html
<div id="newCategory" data-id="new-account" class="modal fade bd-example-modal-lg"
tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="syn-breadcrumb">
<div class="syndicator-form-container">
<form class="syndicator-form" action="{% url 'content:category' %}"
method="post">
{% csrf_token %}
<div class="">
<h3 class="lighter-weight">
{% if user.usertype_is_supplier %}
Request New Category
{% else %}
Add New Category
{% endif %}
</h3>
</div>
<div class="form-fields">
<div class="non-field-errors">
{{ form.non_field_errors }}
</div>
<div id="{{ form.name.name }}" class="d-flex flex-column fields">
<div class="lighter-weight"><label for="id_name">Name</label></div>
<div>{{ form.name }}</div>
<div class="field-errors">{{ form.name.errors }}</div>
</div>
</div>
<div class="submit-button">
<button type="submit" class="btn btn-primary form-control">{% trans 'Submit' %}</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
I want the page to be redirected to an updated list of the categories after the form submit with the success message. As well as show the error message if the category name already exists or if the fields re empty.
You are using self.request instead of request even though request is already passed in post method
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
context['form'] = CategoryModelForm(request.POST or None) # use request.POST
I would rather suggest you to use a FormView or rather a generic view. (docs). You won't need to handle the form by yourself.
#method_decorator(login_required, name='dispatch')
class CategoryView(FormView):
template_name = 'content/category_list.html'
form = CategoryModelForm
success_url = reverse("content:category")
def form_valid(self, form):
self.obj = form.save(commit=True)
messages.success(self.request, 'Successfully created new category.')
return super(CategoryView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(CategoryView, self).get_context_data(**kwargs)
categories = Category.objects.all()
user = self.request.user
category_list = []
for category in categories:
article_count = category.article_count(user)
include = category.show or user.usertype_is_staff() or user.is_superuser
requested_by = category.requested_by if category.requested_by else ''
cat = {
'reference': category.pk,
'name': category.name,
'show': category.show,
'article_count': article_count,
'has_articles': article_count > 0,
'requested_by': requested_by,
'requested_by_name': requested_by.profile.full_name if requested_by and requested_by.profile.full_name
else '-'
}
include and category_list.append(cat)
context['categories'] = category_list
# form will be automatically added to context

How can I change data display in Text Input of ModelChoiceField?

Is there any what to change data display in readonly InputField of ModelChoiceField, but retain the primary key of the object for submitting the form?
views.py
class BookingCreateView(LoginRequiredMixin, CreateView):
login_url = 'login'
form_class = BookingForm
template_name = 'booking_add.html'
success_url = reverse_lazy('booking_list')
def get_initial(self):
initial = super(BookingCreateView, self).get_initial()
initial['date'] = datetime.datetime.strptime(self.request.GET.get('date'), '%d-%m-%Y')
initial['room'] = get_object_or_404(Room, id=self.request.GET.get('room'))
initial['start'] = get_object_or_404(Period, number=self.request.GET.get('start'))
return initial
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
forms.py
class BookingForm(forms.ModelForm):
class Meta:
model = Booking
fields= ['room', 'date', 'start', 'end']
def __init__(self, *args, **kwargs):
initial_args = kwargs.get('initial', None)
if initial_args:
super(BookingForm, self).__init__(*args, **kwargs)
self.fields['room'].widget = forms.TextInput()
self.fields['start'].widget = forms.TextInput()
self.fields['room'].widget.attrs['readonly'] = True
self.fields['date'].widget.attrs['readonly'] = True
self.fields['start'].widget.attrs['readonly'] = True
self.fields['end'].queryset = Period.objects.get_available_periods(
initial_args['room'], initial_args['date'], initial_args['start'])
def clean(self):
cleaned_data = super(BookingForm, self).clean()
now = timezone.localtime(timezone.now())
bookings = Booking.objects.filter(room=cleaned_data['room'], date=cleaned_data['date'])
booking_start_time = datetime.datetime.combine(cleaned_data['date'], cleaned_data['start'].start, timezone.get_current_timezone())
booking_end_time = datetime.datetime.combine(cleaned_data['date'], cleaned_data['end'].end, timezone.get_current_timezone())
for booking in bookings:
if booking.check_overlap(booking_start_time, booking_end_time):
raise forms.ValidationError
if now > datetime.datetime.combine(cleaned_data['date'],
cleaned_data['start'].end, timezone.get_current_timezone()):
raise forms.ValidationError
return cleaned_data
booking_add.html
{% block content %}
<main>
<div class="reg-form">
<form class="form" method="post" action="">
{% csrf_token %}
<label for="room">Phòng</label>
{{ form.room }}
<label for="date">Ngày</label>
{{ form.date }}
<label for="start">Ca bắt đầu</label>
{{ form.start }}
<label for="end">Ca kết thúc</label>
{{ form.end }}
<button type="submit">Đăng ký</button>
</form>
</div>
</main>
{% endblock %}
The page is rendered like this:
The thing I want is that the input below label 'Phòng' which mean Room, filled with the room object method str() not the primary key of Room object and the submitting process still send the primary key.Is there any way to achieve that? Note: the first three fields need to be read only and their data are given via GET request.
I know that however I want the field room to be read-only and select widget doesn't have that attribute.

Form validation failed in django

I have a simple form
forms.py
class CraveDataForm(forms.ModelForm):
class Meta:
model = crave_data
exclude=['date']
class CraveReplyForm(forms.ModelForm):
class Meta:
model = comments
exclude=['date', 'crave']
model.py
class crave_data(models.Model):
#person = models.ForeignKey(User)
post=models.TextField(blank = True,null = True)
date= models.DateTimeField()
def __unicode__(self):
return self.post
class comments(models.Model):
crave=models.OneToOneField(crave_data)
reply=models.CharField(max_length=1000, blank = True,null = True)
date= models.DateTimeField()
def __unicode__(self):
return self.reply
and views.py
def crave_view(request):
if (request.method=="POST"):
form1=CraveDataForm(request.POST, request.FILES)
if form1.is_valid():
crave_made = form1.save(commit=False)
crave_made.person = request.user
crave_made.save()
messages.success(request, 'You Registered Successfully')
else:
messages.error(request, 'Please enter all required fields')
else:
form1=CraveDataForm()
return render(request, "crave/crave.html", { 'form1' : form1 })
def comment_view(request):
if (request.method=="POST"):
form2 = CraveReplyForm(request.POST, request.FILES)
if form2.is_valid():
reply = form2.save(commit=False)
reply.person=request.user
#reply.crave = request.user
reply.save()
else:
messages.error(request, 'Please enter all required fields')
else:
form2 = CraveReplyForm()
return render(request, "crave/comment.html", { 'form2' : form2 })
my template fie crave.html
<form class="horizontal-form" role="form" action="/crave/" method="post" style="padding: 10px;">
{% csrf_token %}
<div class="form-group" >
<div class="col-sm-10">
{{ form1.post.label_tag }}{{ form1.post }} <br /><br>
</div>
</div>
<input type="submit" class="btn btn-success" value="crave" />
</form>
<form action="/crave/reply_get/">
<input type="submit">
</form>
my comment.html
<form class="horizontal-form" role="form" action="/crave/reply_get/" method="post" style="padding: 10px;">
{% csrf_token %}
<div class="form-group" >
<div class="col-sm-10">
{{ form2.reply.label_tag }} {{ form2.reply }} </br> </br>
</div>
</div>
<input type="submit" class="btn btn-success" value="reply" />
</form>
when i click on crave button i want to save data for form one only without saving data for second form. So comments will be for related posts only. Using foreign key. But i am getting the error here in "reply.crave = crave_made" i want to access the crave made from create_view view.
Help me.
Your forms are derived from UserCreationForm so fields of that form should also be present to make it valid.
But I think you want to inherit from ModelForm and not UserCreationForm.
So change your code as
class CraveDataForm(forms.ModelForm):
class Meta:
model = crave_data
exclude=['date']
class CraveReplyForm(forms.ModelForm):
class Meta:
model = comments
exclude=['date', 'crave']

Categories

Resources