Struggling with displaying checkbox data in DetailView page - django

I have a form that when i submit it need to show the details I submitted in the form.
I am really struggling to understand how to get it to display checkbox data.
I went thropugh the django documentation on DetailForms but this didnt really help me with how to display ManyToManyFields.
My template is as follows:
<li>{{theBurger.burger}}</li>
<li>{{theBurger.bun}}</li>
{% for toppings in theBurger.toppings.all %}
<li>{{toppings}}</li>
{% empty %}
<p>No toppings!</p>
{% endfor %}
<li>{{theBurger.sauces}}</li>
{% for extras in theBurger.extras.all %}
<li>{{theBurger.extras}}</li>
{% empty%}
<p>No extras!</p>
{% endfor %}
My view is as followes:
class OrderDetailView(DetailView):
context_object_name = 'theBurger'
slug_field = 'id'
model = models.Burger
def get_context_data(self, **kwargs):
context = super(OrderDetailView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
return context
I can get the page to display all the other information except information that has been submitted via checkboxes. the response that is being sent is:
<QueryDict: {'csrfmiddlewaretoken':
['l6Qq7tg89cueHV2Fl6Qq7tg89cueHV2F2WrzrbJ'],
'burger': ["Aurion's Famous Beef Burger"], 'bun': ['White Bread'],
'toppings': ['15', '1
6'], 'sauces': ['Our Zesty Barbaque Sauce'], 'Submit': ['Git my food!']}>
Lastly here is the form:
class BurgerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BurgerForm, self).__init__(*args, **kwargs)
self.fields['toppings'].widget = forms.CheckboxSelectMultiple()
for field_name in self.fields:
field = self.fields.get(field_name)
if field and isinstance(field , forms.TypedChoiceField):
field.choices = field.choices[1:]
self.fields['extras'].widget = forms.CheckboxSelectMultiple()
class Meta:
model = Burger
fields = ['burger', 'bun', 'toppings', 'sauces', 'extras']
Can someone point out what ive done wrong?

Darn after ploughing though a gazillion google linked I came across this:
http://www.joshuakehn.com/2013/6/23/django-m2m-modelform.html
I tried to remove the:
Commit=False
from:
post = form.save()
in the forms.py file and it works now. I wasted a lot of time on this so I hope it helps someone else.

Related

Django Paginate_by not displaying proper pagination

Hello i have a page using paginate_by 10, and instead i'm getting only 9 elements per page, even tho in the element inspector i see 10 grid spaces my for cicle is just being able to fill 9 out of 10 then it goes into the next page.
Views.py
class VideoListView(generic.ListView):
model = Video
template_name = 'index.html'
context_object_name = 'video_list'
paginate_by = 10
def get_queryset(self):
return Video.objects.order_by('-date')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['category_list'] = Category.objects.all()
return context
EDIT AFTER A QUESTION. here's the template logic
<ul id="first-category" class="items-container">
{% for video in video_list %}
{% if video.home ==True %}
<li class="item">
</li>
{% endif %} {% endfor %}
As possible causes i did find that when i do "paginate by 1" the page starts displaying empty pages for the pages that aren't considered in the IF. which makes me think that the if statement is taking into consideration the empty videos even tho they aren't listed.
How can i fix this?
i do want to filter the videos that aren't meant for the home_page
Thanks a lot in advance for the reply
The reason this happens is because you filter in the template, so after the queryset is paginated. Filtering in the template is not a good idea, for example because it will render the pagination incorrect, but it is also inefficent.
You should filter in the view, with:
class VideoListView(generic.ListView):
model = Video
queryset = Video.objects.filter(home=True).order_by('-date')
template_name = 'index.html'
context_object_name = 'video_list'
paginate_by = 10
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['category_list'] = Category.objects.all()
return context
and thus remove the {% if video.home == True %} … {% endif %} part.

How to query database based on user inputs and show results in another page?

Good day everyone.
I am trying to build a form which queries the database based on user data inputs and then returns the results in a new page. but I don't know exactly how to do it and I am getting errors. I've looked for a solution but couldn't find any. Please help me if you know any solutions.
Thanks in advance.
Here are my codes:
forms.py
class AttendanceForm(forms.Form):
course = forms.CharField(max_length=50)
department = forms.CharField(max_length=10)
semester = forms.IntegerField()
views.py
class AttendanceForm(generic.FormView):
form_class = CrsAttForm
template_name = 'office/crsatt_form.html'
success_url = reverse_lazy('office:members_list')
class MembersList(generic.ListView):
template_name = "office/crs_att.html"
context_object_name = 'members'
def get_queryset(self):
return Members.objects.all()
# I know I should use .filter method but how could I set the parameters to data received from the form
urls.py
url(r'^CourseAttendanceForm/$', views.AttendanceForm.as_view(), name='courseattendance'),
url(r'^CourseAttendanceForm/Results/$',views.MembersList.as_view(), name='memebrs_list'),
I think that it will be easier for you to use function based views for this one.
You can do it like this:
views.py
def form_page(request):
form = AttendanceForm()
# you get to this "if" if the form has been filled by the user
if request.method == "POST":
form = AttendanceForm(request.POST)
if form.is_valid():
course = request.POST['course']
department = request.POST['department']
semester = request.POST['semester']
members = Member.objects.filter(#here you do your filters as you already have the course, department and semester variables)
context = {'members': members}
return render(request, 'second_page.html', context)
# if the form hasn't been filled by the user you display the form
context = {'form': form}
return render(request, 'form_page.html', context)
form_page.html
<form method="post" action="{% url 'form_page' %}">
{% csrf_token %}
{{ form }}
<button type="submit">Search!</button>
</form>
urls.py
path('form_page/', views.form_page, name='form_page')
second_page.html
{% for member in members %}
# here you display whatever you want to
{% endfor %}

Django template isn't rendering dynamic form errors

I have a Django 1.8 form that contains a paragraph tag that renders either some feedback or a question submitted by a user. It also contains a textarea input 'response_text' and a pair of radio buttons 'close_issue'. This response input can be used to send an optional response to the user. If the user submitted some feedback, the admin should be able to click the 'close issue' radio button and submit the form with no response. However, if the textarea input contains a question, then the form should render an error telling the admin that he/she can't submit the form without typing an answer into the response input. The problem I'm having is that I can't get the form to cause the template to render an error message if the user submitted a question but the admin didn't type in a response. My view, model, form, and template are shown below. forms.py shows all the ways (all commented out) I have tried to make the response input field required if the user submitted a question so that the template will display an error. I also tried overriding the default 'clean' method with one that would raise a ValidationError if the user submitted a question and the response input is blank but that didn't work either. Can anyone tell me what I'm doing wrong?
Thanks.
# view.py
def review_feedback_or_question(request, template, *args, **kwargs):
fqid = kwargs['fqid']## Heading ##
submission = FeedbackQuestion.objects.get(pk=fqid)
if request.method == 'POST':
form = FeedbackQuestionResponseForm(request.POST, submission=submission)
if form.is_valid():
# process the form
return redirect('review-feedback-or-question-queue')
else:
pass
form = FeedbackQuestionResponseForm(submission=submission)
context = {'form': form, 'submission': submission,}
return render(request, template, context)
# models.py
class FeedbackQuestion(models.Model):
SELECT = ''
FEEDBACK = 'feedback'
QUESTION = 'question'
SUBMISSION_TYPE_CHOICES = (
(SELECT , '-- Select --'),
(FEEDBACK, 'Feedback'),
(QUESTION, 'Question'),
)
user = models.ForeignKey(User, related_name="user")
submission_type = models.CharField(max_length=8,
choices=SUBMISSION_TYPE_CHOICES,
default=SELECT)
submission_text = models.TextField()
date_submitted = models.DateTimeField(auto_now_add=True)
response_text = models.TextField()
respondent = models.ForeignKey(User, related_name='respondent')
date_responded = models.DateTimeField(auto_now=True)
issue_closed = models.BooleanField(default=False)
class Meta:
db_table = 'feedback_question'
# forms.py
class FeedbackQuestionResponseForm(forms.Form):
TRUE = 1
FALSE = 0
BLANK = ''
CHOICES = ( (TRUE, 'Yes'), (FALSE, 'No') )
response_text = forms.CharField(
required=False,
label='',
widget=forms.Textarea(attrs={'placeholder': 'Enter response...'}))
close_issue = forms.TypedChoiceField(
choices=CHOICES,
label='Close this issue?',
widget=forms.RadioSelect(renderer=HorizontalRadioRenderer),
coerce=int)
def __init__(self, *args, **kwargs):
if 'submission' in kwargs:
submission = kwargs.pop('submission')
if submission.submission_type == 'question':
# NONE OF THESE WORKED!
#self.fields.get('response_text').required = True
#self.declared_fields['response_text'].required = self.TRUE
#self.declared_fields['response_text'].required = self.TRUE
#self.declared_fields['response_text'].required = True
#self._errors['response_text'] = "You must enter a response"
pass
super(FeedbackQuestionResponseForm, self).__init__(*args, **kwargs)
# template.html
<p>{{ submission.submission_text }}</p>
<form action="" method="post">{% csrf_token %}
{{ form.non_field_errors }}
{% if form.errors %}
{% if form.errors.items|length == 1 %}
Please correct the error below.
{% else %}
Please correct the errors below.
{% endif %}
</p>
{% endif %}
{{ form.response_text.errors }}
{{ form.response_text.label_tag }} {{ form.response_text }}
{{ form.close_issue.errors }}
{{ form.close_issue }} {{ form.close_issue.label_tag }}
<input type="submit" value="Submit" class="" />
</form>
You're not passing submission into the form when you instantiate it on POST, so the required attribute is never being set.
Daniel Roseman was correct in that I need to pass 'submission' into the form when I instantiate the form on POST. But there were still two other problems. First, I need to instantiate the form inside the else block. If this isn't done and the form doesn't validate, then you're passing an unbound form back to the viewer and any errors won't be displayed. Also, it isn't necessary to pass 'submission' to the form when you instantiate it here:
...
else:
form = FeedbackQuestionResponseForm()
context = {...}
...
The next problem was that the order of my statements inside the init method was incorrect. It appears that I needed to execute 'super()' before trying to reference the 'response_text' field. I'll need to locate and study this method in the Django source code to understand exactly why. In any case, this works:
def __init__(self, *args, **kwargs):
if 'submission' in kwargs:
submission = kwargs.pop('submission')
else:
submission = False
super(FeedbackQuestionResponseForm, self).__init__(*args, **kwargs)
if submission:
if submission.submission_type == 'question':
self.fields['response_text'].required = True
else:
self.fields['response_text'].required = False
When the above changes are implemented, the form will make the response_text field required if the user submits a question and an error will be displayed if the admin doesn't enter a response before submitting the form. Many thanks again to Daniel for getting me back on track towards finding a solution.

django crispy forms file upload

I'm having trouble getting an image field to upload correctly with django crispy forms. Everything else on the form and page works fine, but the image field does not error or save, it just passes right over it. I am able to add the image through admin just fine, it only fails on my crispy form.
#views.py
#login_required
def dashboard(request, **kwargs):
'''
perform managerial and administrative tasks for a blog. Only
available to the blog owner
'''
alert_message = ""
status = ""
blog_slug = kwargs['blog']
blog = get_object_or_404(PersonalBlog, slug=blog_slug)
# handle the form submit to update blog data
form = BlogEditForm(instance=blog)
if request.method == "POST":
if blog.owner == request.user:
form = BlogEditForm(request.POST, instance=blog)
if form.is_valid():
form.save()
alert_message = "Your blog data has been updated successfully"
status = "saved"
else:
alert_message = "There was a problem saving the data you entered. Please correct the errors above."
status = "error"
else:
alert_message = "You do not have access to update this blog's information."
status = "error"
return render(request, "blogcontent/dashboard.html", {'alert_message':alert_message,
'status':status, 'form':form})
#forms.py
class BlogEditForm(ModelForm):
description = forms.CharField(widget = forms.Textarea())
twitter = forms.CharField(required=False, help_text="show twitter feed, and allow people to interact with you on twitter")
twitter_widget_id = forms.CharField(required=False, help_text="required to show a timeline widget for your twitter account. " + \
"<span class='glyphicon glyphicon-question-sign'></span>")
instagram = forms.CharField(required=False, help_text="show instagram feed on your blog page")
disqus = forms.CharField(required=False, help_text="allow comments at the bottom of your blog posts")
class Meta:
model = PersonalBlog
def __init__(self, *args, **kwargs):
super(BlogEditForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
'<h2 class="text-center">Edit data about your blog</h2>',
Field('owner', type="hidden"),
Field('title', type="hidden"),
Div('description', css_class="col-md-12"),
Div('twitter', css_class="col-md-6"),
Div('twitter_widget_id', css_class="col-md-6"),
Div('instagram', css_class="col-md-6"),
Div('disqus', css_class="col-md-6"),
Div('logo', css_class="col-md-12"),
),
ButtonHolder(
Submit('submit', 'Update Info', css_class='btn-lg'),
css_class="text-center"
),
)
#dashboard.html
<form method="post" enctype="multipart/form-data">
{% crispy form %}
</form>
Ah, I knew it was going to be something dumb. I was not attaching the request.FILES to the form.save() object.
form = BlogEditForm(request.POST, request.FILES, instance=blog)
Have you tried changing:
<form method="post" enctype="multipart/form-data">
{% crispy form %}
</form>
To just:
{% load crispy_forms_tags %}
{% crispy form %}
Also in your code, your are calling:
if form.is_valid():
form.save()
You don't want to save() the form. You want to save the form data to a model instance. So create() a model using the form data. This may be why as well. Forms are just used to hold data until you clean it and save it to the database using a model.

django-extra-views and SortableListMixin configuration confusion

I am using django-extra-views in order to have sortable tables in my Django ListViews.
I'm not 100% sure of why I can't get it working, but I've always found working from tests.py difficult wrt templates.
So I have this in my views.py
class PartTypePartList(SortableListMixin, generic.ListView):
model = PartNumber
template_name = 'inventory/newparttype_list.html'
sort_fields = ['name',]
paginate_by = 25
def get_queryset(self):
self.parttype = self.kwargs['parttype']
return PartNumber.objects.filter(fds_part_type=self.parttype)
def get_context_data(self, **kwargs):
context = super(PartTypePartList, self).get_context_data(**kwargs)
context['parttype'] = self.parttype
return context
And in urls.py
url(r'^newparttype/(?P<parttype>\d{2})/$', views.PartTypePartList.as_view(), name='new_part_type_view'),
And with these two we are getting the list as expected.
In the relevant template:
Name
asc name
desc name
{% if sort_helper.is_sorted_by_name %} ordered by name {{ sort_helper.is_sorted_by_name }} {% endif %}
The issue is that there is no sorting happening. In particular,
{{ sort_helper.get_sort_query_by_name }} and
{{ sort_helper.get_sort_query_by_name_asc }} and
{{ sort_helper.get_sort_query_by_name_desc }}
each return an empty string.
What am I doing wrong?
I was using django-tables2 but the owner admitted he would not be continuing dev on it and I'm not skilled enough or time rich enough to take it on myself.
[EDIT]
I believe this still deserves a solution, but I've re-written the view to be a FBV rather than a CBV and am manipulating the data accordingly
[/EDIT]
You need to call get_queryset parent method:
def get_queryset(self):
self.parttype = self.kwargs['parttype']
qs = super(PartTypePartList, self).get_queryset()
qs = qs.filter(fds_part_type=self.parttype)
return qs