Django form error message showing despite selecting something from dropdown - django

I have a Django model which is:
class Account(models.Model):
name = models.CharField(max_length=50, blank=True)
number = models.CharField(max_length=16, blank=True)
I'd like to create a form where user can select an existing account's phone number from a dropdown list. So in forms.py, I have:
class AccountSelectForm(forms.Form):
phone_num_err_msgs = {'required': "You must select a phone number to send this message."}
phone_number = forms.CharField(required=True, error_messages=phone_num_err_msgs)
selected_group_ids = forms.CharField(required=True, widget=forms.HiddenInput)
launch_datetime = forms.CharField(required=True)
In views.py, I have:
class AccountSelectView(LoginRequiredMixin, FormView):
template_name = 'campaigns/send.html'
form_class = AccountSelectForm
success_url = reverse_lazy('campaigns:taskq_list')
def get_context_data(self, **kwargs):
data = super(AccountSelectView, self).get_context_data(**kwargs)
data['groups'] = Group.objects.all()
data['campaign'] = Campaign.objects.get(id=self.request.GET['cam_id'])
data['accounts'] = Account.objects.all()
return data
def form_valid(self, form):
# If we insert pdb, we never reach here
#import pdb
#pdb.set_trace()
data = form.cleaned_data
campaign_id = self.request.GET['cam_id']
# ... do other form validation stuff here
return super(ConversationSendView, self).form_valid(form)
In send.html, I have:
<form action="" method="post">
{% csrf_token %}
<!-- A couple of other fields to collect user input -->
<div class="form-group">
<p><b>Step 3: Select aphone number to send the message FROM:</b></p>
{{ form.phone_number.errors }}
<select id="phone" style="width: 380px;">
<option value="">--------</option>
{% for a in accounts %}
<option value="{{ a.id }}">{{ a.number }}</option>
{% endfor %}
</select>
<div class="page-btns">
<input type="submit" class="btn btn-primary" value="Send Message to Selected Group(s)" />
</div>
</form>
But despite selecting the entry from the dropdown list (and all other required forms) before submitting, I keep seeing the phone_num_err_msgs on the HTML page [please see the screenshot here].
Is there something that I'm missing? Where (which file) can I import pdb and see why it is returning an error? I'm new to Django, so this is very likely a silly mistake/overlook. Thanks in advanced for the answers!

There are a few things wrong here. The immediate cause is that you are missing name="phone_number " in your select tag, so the browser is not sending any data for that element.
But it is not clear why you are constructing that element manually anyway. Rather than defining a CharField and ignoring it, you should be using a ModelChoiceField, which will automatically give you a select box with all the accounts in.
class AccountSelectForm(forms.Form):
...
phone_number = forms. ModelChoiceField(queryset=Account.objects.all())
...
{{ form.phone_number.errors }}
{{ form.phone_number }}

Related

Python/Django - radiobutton with submit button instead of buttons

I have a Django application where I want to have a setting: if the email is sent automatically or manually. What I have works, but not in the style that I want it.
At the moment I have 2 buttons where you can click them and the setting is set to what you want. But what I would want is radio buttons, that are also already checked or unchecked, depending on the setting.
What I have now is:
model.py
class AutoSendMail(models.Model):
auto = models.BooleanField(default=False)
manual = models.BooleanField(default=True)
send_type = (
('manual', 'MANUAL'),
('auto', 'AUTO')
)
type = models.CharField(max_length=6, choices=send_type, default="manual")
forms.py
CHOICES = [('manual', 'MANUAL'),
('auto', 'AUTO')]
class SendMailSetting(ModelForm):
class Meta:
model = AutoSendMail
fields = ['auto', 'manual', 'type']
widgets = {
"manual": forms.RadioSelect(attrs={'class': 'form-control'}),
"auto": forms.RadioSelect(attrs={'class': 'form-control'}),
'type': forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect)
}
views.py
class AutoSendView(generic.TemplateView):
template_name = 'core/mailbox/autoSendMail.html'
context_object_name = 'autosend'
extra_context = {"mailbox_page": "active"}
form_class = SendMailSetting
def post(self, request, *args, **kwargs):
if request.POST.get('manual'):
logger.info("Set to: manual email send")
AutoSendMail.objects.filter(pk=1).update(auto=True,
manual=False,
type="manual")
elif request.POST.get('auto'):
logger.info("Set to auto email send")
AutoSendMail.objects.filter(pk=1).update(auto=True,
manual=False,
type="auto")
return HttpResponseRedirect(self.request.path_info)
autoSendMail.html
<form class="card" action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="card-body">
<h3 class="card-title">Update Mail Settings</h3>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="form-label">Send E-mail</label>
<button type="radio" name="manual" value="manual" class="btn btn-primary">Manual</button>
<button type="radio" name="auto" value="auto" class="btn btn-primary">Automated</button>
</div>
</div>
</div>
</div>
</form>
Currently it looks like this:
And I would like it to look more like this:
At the moment I'm only using a POST request, and I guess, to inform the user I also need to use a GET request, but I can't get it to work. Also I'm now not using the form, I tried to use it in different ways but I can't get the result I want..
Can someone help me?
You had created a modelForm but you are not using it here. Anyway for basic method you can try below method
<input type="radio" name="update_type" value="manual">Manual</input>
<input type="radio" name="update_type" value="auto">Automated</input>
<button type="submit" class="btn btn-primary">Submit</button>
views.py
def post(self, request, *args, **kwargs):
update_type = request.POST.get('update_type'):
if update_type == 'manual':
"update db with manual to true"
else:
"update the db with auto to true"

Save and Update data from custom html forms in django

I've created a custom HTML form for my model, just like I want to add a post from the front-end. I already created a page with the name add-post.html
<form method="POST" action="">
{% csrf_token %}
<input name="title" type="text">
<textarea spellcheck="false" name="description"></textarea>
<input type="file" name="image" #change="fileName" multiple />
<select required name="priority">
<option value="Low" selected>Low</option>
<option value="Medium">Medium</option>
<option value="High">High</option>
</select>
<input type="checkbox" name="on_hold">
<button type="submit">Add ToDo</button>
</form>
Here's my model.py
class Todo(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(null=True, blank=True, upload_to='todo/images/')
description = RichTextField()
Low, Medium, High = 'Low', 'Medium', 'High'
priorities = [
(Low, 'Low'),
(Medium, 'Medium'),
(High, 'High'),
]
priority = models.CharField(
max_length=50,
choices=priorities,
default=Low,
)
on_hold = models.BooleanField(default=False)
No, I want to use the above custom HTML form to post data and save it to this model database. instead of using {% form.as_p %}
And I also created a particular page to update this post from the front-end but don't know how to make it work.
Can you please guide me on how can I save data from the custom form and also update it from the custom form?
Appreciate your response :)
#Mubasher Rehman - You are almost there
forms.py
class TodoCreationForm(forms.ModelForm):
class Meta:
model = Todo
fields = ('title','image','description','priorities','priority','on_hold',)
views.py
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic import CreateView
class CreatProduct(SuccessMessageMixin,CreateView):
model = Todo
form_class = TodoCreationForm
template_name = "add_post.html"
success_message = "Todo was created successfully"
error_message = "Error saving the Todo, check fields below."
add_post.html
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
#Mubasher Rehman - I fought this problem myself for awhile and finally found a solution. My situation was much different than yours, but try this:
In your views.py overwrite the form_valid method like so:
def form_valid(self, form):
if self.request.POST:
if form.is_valid():
t= Todo.objects.create(title='title', image='image', description='description', priority='priority', on_hold='on_hold')
t.save()
return super(ModelView, self).form_valid(form)

How to create an attendance system with django?

Currently i am working a project for making a student management system for my college.
I have a User model and a profile model. I also added an attendance model with User as the foreign key. I was stuck in while i started writing the form for entering the attendance.
class Attendance(models.Model):
Student = models.ForeignKey(User, on_delete=models.CASCADE)
Hour = models.CharField(max_length=1, blank=False)
Subject = models.CharField(max_length=8, blank=False)
Date = models.DateTimeField(default=timezone.now)
Presence = models.BooleanField(default=False, blank=False)
def __str__(self):
return f'{self.Student}'
This is my template where query set is names of Users that should be the default value. The number of Users, the number of forms should come. With this template i can create only one object, with the values of the last form iterated. When the <form> is inside forloop i get multiple objects with the same values of lastly iterated form.
<form method="POST" action="{% url 'academics' %}" style=" padding: 5%">
{% csrf_token %}
{% for query in queryset %}
<input type="text" name="Student" class="form-control" required id="id_Student" value="{{query}}">
<input type="text" name="Hour" class="form-control" required id="id_Hour">
<input type="text" name="Subject" class="form-control" required id="id_Subject">
<input type="checkbox" name="Presence" required id="id_Presence">
{% endfor %}
<button type="Submit">Submit</button>
</form>
I came to know about formsets, but i don't know how to implement for a complex thing like this. This is my views.py:
def academics(request):
if request.user.is_staff:
form = forms.AttendanceForm()
context = {
'form': form,
'queryset': User.objects.filter(profile__Year='SY',profile__Department='CSE')
}
if request.method == "POST" :
form = forms.AttendanceForm(request.POST)
if form.is_valid():
student = request.POST.get('Student')
hour = request.POST.get('Hour')
subject = request.POST.get('Subject')
boolean = request.POST.get('Presence')
def bool(boolean):
if boolean == 'on':
return 'True'
else:
return 'False'
form = Attendance(Student=student,Hour=hour,Subject=subject,Presence=bool(boolean))
form.save()
return render(request, 'console/academics.html',context)
else:
context = {
'attends': Attendance.objects.all().exclude(Date=timezone.now()),
'todays': Attendance.objects.filter(Date=timezone.now())[:8]
}
return render(request, 'student/academics.html',context)
Can anyone alter the code on how to use formset here. I know am asking an open help instead of asking doubts. Atleast give me hints or correct video tutorial links please!

Django - Forms - autofill & hide foreign key field

I have models.py, and forms.py that looks like this:
class BHA_overall(models.Model):
bha_number = models.ForeignKey(BHA_List, 'CASCADE', related_name='bha_overall')
drill_str_name = models.CharField(max_length=111)
depth_in = models.CharField(max_length=111)
depth_out = models.CharField(max_length=111)
class BHA_overall_Form(forms.ModelForm):
class Meta():
model = BHA_overall
fields = '__all__'
In my template, if I just use:
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button name='action' value='login' type="submit">Sign in</button>
</form>
the foreign key field bha_number is displayed as a combo box where I can select the specific bha_number model instance it belongs to, like this:
Here, I want to remove Bha number field from the user side, and just let my code auto fill that field for the user, and hide it. So from the user side, there will be only 3 fields displayed. How can I do this?
Currently I am implementing this html code:
<form id="demo-form" data-parsley-validate="" novalidate="" method="POST">
<div class="row">
{% csrf_token %}
{% for field in form %}
<div class="col-lg-2 col-md-2 col-sm-4 col-xs-6" style="margin-bottom: 5px">
<label class="input-upper-title">{{ field.name }}</label>
<input type="text" id="" class="form-control input-field-height-vertical" name="" data-parsley-trigger="" required="">
</div>
{% endfor %}
<input type="submit" class='btn btn-primary' value="Submit">
</div>
</form>
And it renders this:
I want the first field, bha_number to disappear from the user side, but the system still needs to get that information to save to a correct model instance. So I'm looking for an way to auto fill this ForeignKey field at forms.py or views.py level.
Here is my views.py:
class BHA_UpdateView(UpdateView):
model = BHA_List
success_url = reverse_lazy('well_list') # this is wrong
form_class = BHA_overall_Form
def post(self, request, **kwargs):
api = get_well_api(self.request)
current_bha = BHA_List.objects.filter(id=get_current_bha_id(self.request))[0]
form = BHA_overall_Form(request.POST, instance=BHA_overall.objects.filter(bha_number__well__api=api, bha_number=current_bha)[0])
if form.is_valid():
form.save()
return super().post(request, **kwargs)
You can use exclude to hide the field from form
Class BHA_overall_Form(forms.ModelForm):
class Meta():
model = BHA_overall
fields = '__all__'
exclude = ('bha_number',)
To auto fill after checking if form is valid, just clean the data using form = form.cleaned_data and store it in any variable. It's nothing but a dictionary. You can assign value to this like form['bha_number'] = your value and save it to database by using form.save().
Or you can use object = form.save(commit=False) because this method will return an object. Then you can do object.bha_number = your number
And finally object. Save in next line. That's all. Choose whatever solution you like.
Why don't you keep it as is on forms.py but exclude it from your HTML? That way the user would not see it as an option but the value would still be sent with the form.

Why is Django widgets for TimeInput not showing

I'm trying to create a TimeInput field in a form and noticed that the widget isn't showing correctly. But when I check the localhost:8000/admin, I see the widget showing up correctly.
My code is as follows. For models.py,
class TimeLimit(models.Model):
before = models.TimeField(blank=True, default=time(7, 0)) # 7AM
after = models.TimeField(blank=True, default=time(23, 0)) # 11PM
For views.py,
class UpdateTimeLimitView(LoginRequiredMixin, FormView):
model = TimeLimit
template_name = 'accounts/update_time_limit.html'
form_class = UpdateTimeLimitForm
def get_success_url(self):
return reverse_lazy('accounts:user_profile') + '?username=' + self.request.GET['username']
def get_context_data(self, **kwargs):
data = super(UpdateTimeLimitView, self).get_context_data(**kwargs)
data['username'] = self.request.GET['username']
return data
For forms.py,
class UpdateTimeLimitForm(forms.Form):
time_error = {'required': 'This field is required.',
'invalid': 'Please enter valid Hour:Minute values.'}
before = forms.TimeField(widget=forms.TimeInput(format='%H:%M'))
after = forms.TimeField(widget=TimeInput(format='%H:%M'))
class Meta:
model = TimeLimit
Finally, the relevant part for fields in update_time_limit.html,
<div class="container">
<form method="post">
{% csrf_token %}
<p>
{% for field in form %}
{{ field.errors }}
<label for="{{ field.id_for_label }}">{{ field.label }}({{ field.help_text }}):</label>
<br />
{{ field }}<br /><br /> and
{% endfor %}
</p>
<input class="btn btn-primary done-btn" type="submit" value="Update Time Limit">
</form>
</div>
Is there anything that I'm missing or doing wrong? Thank you.
The Django admin uses AdminTimeWidget to display time fields, not the TimeInput widget that you are using in your code.
There isn't a documented way to reuse the AdminTimeWidget outside of the Django admin. Getting it to work is very hacky (see the answer on this question, which is probably out of date), so it's probably better to use a different widget.
convert datetime.time(7, 0) to string work for me.
data['before'] = data['before'].strftime('%H:%M:%S')