Django - How to display multpile choices for Polls app - django

Following along the Django polls app tutorial, I was wondering if instead of having a Charfield for the choice Model and manually adding every response/choice to the database; Is it possible to have choices?
For example:
class Poll(models.Model):
text = models.CharField(max_length=255)
pub_date = models.DateField()
def __str__(self):
return self.text
class Choice(models.Model):
question = models.ForeignKey(Poll, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=255)
votes = models.IntegerField(default=0)
def __str__(self):
return "{} - {}".format(self.question.text[:25],
self.choice_text[:25])
You have standard choices for every Poll like this:
class Poll(models.Model):
text = models.CharField(max_length=255)
pub_date = models.DateField()
def __str__(self):
return self.text
class Choice(models.Model):
VOTING_CHOICES = (
('Aye', 'Aye'),
('Nay', 'Nay'),
('Abstain', 'Abstain'),
)
question = models.ForeignKey(Poll, on_delete=models.CASCADE)
choice_text = models.CharField(
max_length=7,
choices=VOTING_CHOICES,
default='Aye',
)**
votes = models.IntegerField(default=0)
def __str__(self):
return "{} - {}".format(self.question.text[:25],
self.choice_text[:25])
Poll Detail page
========
{{ poll }}
<form action="" method="post">
{% csrf_token %}
{% for choice in poll.choice_set.all %}
{% for i,k in choice.VOTING_CHOICES %}
<input type="radio"
name="option"
id="choice{{forloop.counter}}"
value="{{ i }}"/>
<label for="choice{{forloop.counter}}">{{ k }}</label>
{% endfor %}
{% endfor %}
<input type="submit" value="Vote">
</form>
views.py
def poll_detail(request, poll_id):
#render poll detail page
poll= get_object_or_404(Poll, id=poll_id)
if request.method =="POST":
print(request.POST)
# Debug to see what data I am posting on form submission
context = {
'poll':poll,
}
return render(request, 'app/poll_detail.html', context)
Does that make sense? Every time I try to implement this, I either get an empty dictionary response when I POST from the form or the options show up in tuples(or do not render at all).

Related

Django forms how to display related data in an inner form

I am struggling with Django forms.
I have the following model.py:
class Property(models.Model):
portfolio = models.ForeignKey("portfolios.Portfolio", on_delete=models.CASCADE)
class PropertyImage(models.Model):
property = models.ForeignKey("Property", on_delete=models.CASCADE)
image = models.ImageField(upload_to = property_image_upload_to)
def __str__(self):
return self.image.url
class PropertyDocument(models.Model):
property = models.ForeignKey("Property", on_delete=models.CASCADE)
document = models.FileField()
class Address(models.Model):
property = models.OneToOneField("Property", on_delete=models.CASCADE)
line1 = models.CharField(max_length=100)
line2 = models.CharField(max_length=100, null=True, blank=True)
line3 = models.CharField(max_length=100, null=True, blank=True)
post_code = models.CharField(max_length=7)
town = models.CharField(max_length=100, null=True, blank=True)
city = models.CharField(max_length=100)
When adding/updating a property, I want the form to show the form for related objects like the address, documents/images instead of the select list's that appear in forms - I want to be able to add/edit the related data.
My view.py file
class PropertyCreate(CreateView):
model = Property
form_class=PropertyAddressFormSet
success_url = reverse_lazy('Property_list')
def get_context_data(self, **kwargs):
data = super(PropertyCreate, self).get_context_data(**kwargs)
return data
Property_form.html
{% extends 'base/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" class="btn btn-primary" />
<button class="btn btn-link" onclick="javascript:history.back();">Cancel</button>
</form>
{% endblock %}
urls.py
from . import views
app_name = 'properties'
urlpatterns = [
path('<int:portfolio_id>/<int:pk>/edit', views.PropertyUpdate.as_view(), name='property_edit'),
path('<int:portfolio_id>/create', views.PropertyCreate.as_view(), name='property_new'),
]
I've read about inlineformset_factories and inlineformset's etc, but is this the best choice for my scenario? If so, I can't figure out how to show the portfolio, address form
I;m currently using a inlineformset like so, which creates the Address form on the PropertyCreate view, but I want to also add in the PropertyImages and PropertyDocs to the ProertyCreate view.:
PropertyAddressFormSet = inlineformset_factory(
parent_model=Property,
model=Address,
form=AddressForm,
extra=0,
min_num=1
)
For anyone in the same boat as me, I managed to get this working with the following code:
Forms.py:
class PropertyForm(ModelForm):
""" Edit a property """
class Meta:
model = Property
exclude = ()
PropertyAddressFormSet = inlineformset_factory(
parent_model=Property,
model=Address,
form=AddressForm,
extra=0,
min_num=1
)
Views.py
class PropertyCreate(CreateView):
model = Property
form_class=PropertyForm
success_url = reverse_lazy('Property_list')
def get_context_data(self, **kwargs):
data = super(PropertyCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['address'] = PropertyAddressFormSet (self.request.POST, instance=self.object)
else:
data['address'] = PropertyAddressFormSet ()
return data
template:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form |crispy }}
<fieldset class="border p-2">
<legend class="w-auto">Address</legend>
{{ address.management_form }}
{% for form in address.forms %}
<div >
{{ form.as_p }}
</div>
{% endfor %}
</fieldset>
</form>
Hope this helps someone.

In Django getting errors based on dropdown selection

Getting error when fields are selected from the down. Not seeing why it is throwing error
Django Dropdown form.error: ERROR ALERT:
location_name Select a valid choice. That choice is not one of the available choices.
Here is the model, form, view and html looks like
MODEL
class Code (models.Model):
name = models.CharField(max_length=4, default=None, blank=True)
def __str__(self): return self.name
class Device (models.Model):
code = models.ForeignKey(Code, on_delete=models.CASCADE, null=True)
ip = models.GenericIPAddressField(protocol='IPv4', unique=True)
def __str__(self): return self.ip
class SiteData (models.Model):
site = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
site_ip = models.ForeignKey(Device, on_delete=models.SET_NULL, null=True)
site_data1 = models.CharField(max_length=3, default='120')
class CombineData(models.Model):
location = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
device = models.ForeignKey(AddData, on_delete=models.SET_NULL, null=True)
locdata = models.ForeignKey(SiteData, on_delete=models.SET_NULL, null=True)
FORM
class CombineData_form(forms.ModelForm):
class Meta:
model = P2_NexusLeafPair
fields = '__all__'
VIEW
def comboView(request, *args, **kwargs):
template_name = 'site_display.html'
code = Code.objects.order_by('-name')
device = Device.objects.order_by('-ip')
sitename = SiteData.objects.order_by('-site')
content = {
'code': code,
'device': device,
'sitename': sitename
}
if request.method == 'POST':
form = CombineData_form(request.POST or None)
print(form.is_valid())
#print(form.errors)
if form.is_valid():
. . .
else:
messages.error(request, form.errors)
else:
form = CombineData_form()
return render(request, template_name, content)
HTML
<form id="comboView" class="post-form" role=form method="POST" action="comboView">{% csrf_token %}
<div name="code" class="dropdown" id="mainselection" required>
<select name="dc_name">
<option class="dropdown-item" value="">---------</option>
{% for item in code %}
<option value="{{ item }}">{{ item }}</option>
{% endfor %}
</div>
<input type="submit" value="Submit" />
</form>
{same as other fields: device, sitename}
{% for item in content %}
{{item.code}}
{% endfor %}
Try that.

How can I grab the (text) value instead of the number on a multiple choice field?

I have a CreateView class and I'm trying to use the input from a multiple choice field it has as part of the success_url.
It works on my TopicCreateView class because the input is a charfield, but when I try to get it to work on PostCreateView it returns a KeyError. From what i understand it's because it returns the value of the multiple choice field(1, 2, 3 etc) instead of the text between the option tags.
The Topic part works fine.
views.py
class TopicCreateView(LoginRequiredMixin, CreateView):
model = Topic
template_name = 'topic_form.html'
fields = ['board', 'title']
success_url = '/topic/{title}'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
And here is the models.py
class Topic(models.Model):
title = models.CharField(max_length=100, unique=True)
board = models.ForeignKey(Board, default='ETC', on_delete=models.SET_DEFAULT)
date_published = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
Here is what I can't get to work, the Post.
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
template_name = 'post_form.html'
fields = ['topic', 'content']
success_url = '/topic/{topic}'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
and models.py
class Post(models.Model):
content = models.TextField()
author = models.CharField(max_length=200, default='Unknown', blank=True, null=True)
date_published = models.DateTimeField(default=timezone.now)
topic = models.ForeignKey(Topic, default=content, on_delete=models.SET_DEFAULT)
def __str__(self):
return self.topic
Also, the form is the same for both of them:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Create A New Post</legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Submit</button>
</div>
</form>
</div>
{% endblock %}
So, when I redirect to the newly created topic/thread it works, but I can't do the same for new posts.

Django QuerySet FIlter based on previous QuerySet

This may be a simple answer but after days of searching I cannot seem to figure out the correct way to achieve this.
I have a template where I want to show all the questions that are related to an assessment that has been assigned to a user. I thought that I could use the results from:
ResponseDetails = AssessmentResponse.objects.prefetch_related('assessment').filter(id=response_id)
by looking into the object and grabbing the assessment_id which I could then pass into the next query-set as a filter but I couldn't get that to work.
Problem: Because the view doesn't filter based on the assessment_id found in the AssessmentResponse model, it gives me every question in the AssessmentQuestion model.
An answer would allow me to actually have a good nights sleep trying to figure it out.
Views
def UpdateAssessmentResponse(request, response_id):
ResponseDetails = AssessmentResponse.objects.prefetch_related('assessment').filter(id=response_id)
QuestionList = AssessmentQuestion.objects.all()
ChoiceList = AssessmentQuestionChoice.objects.all()
context = {
"ResponseDetails":ResponseDetails,
"QuestionList":QuestionList,
"ChoiceList": ChoiceList,
#"ID" : ID,
}
return render(request, "assessment/assessment_response_update.html", context)
Template
{% if QuestionList and ResponseDetails%}
{% csrf_token %}
{% for question in QuestionList %}
<p> {{ question.question_text }} </p>
{% for choice in ChoiceList %}
{% if choice.question_id == question.pk %}
<fieldset id="group1">
<div class="custom-control custom-radio custom-control-inline">
<input type="radio" class="custom-control-input" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label class="custom-control-label" for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label>
</div>
<fieldset id="group1">
{% endif %}
{% endfor %}
{% endfor %}
<div class="card-footer">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
{% else %}
<p>There are currently no questions for this assessment.</p>
{% endif %}
Models:
class AssessmentForm(models.Model):
name = models.CharField(max_length=400)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
is_published = models.BooleanField()
due_date = models.DateField(default=datetime.now, blank=True, null=True)
def __str__(self):
return self.name
class AssessmentResponse(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
assessment = models.ForeignKey('AssessmentForm', on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
last_update = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return reverse('assessment_response_detail', args=[str(self.id)])
#def get_assessment_id(self):
# return self.assessment
def __str__(self):
return self.user
class AssessmentQuestionType(models.Model):
type = models.CharField(max_length=30)
def __str__(self):
return self.type
class AssessmentQuestionCategory(models.Model):
name = models.CharField(max_length=200)
assessment = models.ForeignKey('AssessmentForm', on_delete=models.CASCADE)
def __str__(self):
return self.name
class AssessmentQuestionSubCategory(models.Model):
name = models.CharField(max_length=200)
parent_category = models.ForeignKey('AssessmentQuestionCategory', on_delete=models.CASCADE)
def __str__(self):
return self.name
#CHOICES_HELP_TEXT = _(u"""The choices field is only used if the question type if the question type is 'radio', 'select', or 'select multiple' provide a comma-separated list of options for this question .""")
class AssessmentQuestion(models.Model):
question_type = models.ForeignKey('AssessmentQuestionType', on_delete=models.PROTECT)
question_text = models.TextField()
is_required = models.BooleanField()
category = models.ForeignKey('AssessmentQuestionCategory', on_delete=models.PROTECT, blank=True, null=True)
subcategory = models.ForeignKey('AssessmentQuestionSubCategory', on_delete=models.PROTECT, blank=True, null=True)
assessment = models.ForeignKey('AssessmentForm', on_delete=models.CASCADE)
def __str__(self):
return self.question_text
class AssessmentQuestionChoice(models.Model):
question = models.ForeignKey(AssessmentQuestion, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200, blank=True, null=True)
def __str__(self):
return self.choice_text
class AssessmentAnswer(models.Model):
text = models.CharField(max_length=1024)
question = models.ForeignKey('AssessmentQuestion', on_delete=models.CASCADE)
response = models.ForeignKey('AssessmentResponse', on_delete=models.PROTECT)
def __str__(self):
return self.text
Figured it out! Objects.filter is a lazy query so it wasn't actually available to my other query-set to use as a filter. Solved it by using objects.get instead.

form.is_valid() always returns false in views.py

form.is_valid() always fails. I tried different ways to handle it but fails every time and it returns false. Please help in figuring out whats wrong with the code.
models.py looks like this -
class Album(models.Model):
album_name = models.CharField(max_length=50, primary_key=True)
place = models.CharField(max_length=50)
date_pub = models.DateTimeField('date published')
def __str__(self):
return self.album_name
class Images(models.Model):
album_name = models.ForeignKey(Album, db_column='album_name')
image_name = models.CharField(max_length=40)
image = models.FileField(null=True, blank=True)
upload_dt = models.DateTimeField(auto_now=True, auto_now_add=False)
like_cntr = models.IntegerField(default=0)
description = models.CharField(max_length=200, null=True)
def __str__(self):
return self.image_name
forms.py is -
class ImagesForm(forms.ModelForm):
description = forms.CharField(required=False)
class Meta:
model = Images
fields = ('album_name', 'description',)
views.py is -
class RandomView(TemplateView):
template_name = 'photos/random.html'
def get(self, request, album_name):
images = Images.objects.filter(album_name=album_name)
context = {'album_name':album_name, 'images' : images}
return render(request, 'photos/random.html', context)
def post(self, request, album_name):
form = ImagesForm(request.POST)
if form.is_valid():
form.save(commit=False)
text = form.cleaned_data['description']
Images.album_name = album_name
form.save()
else:
return HttpResponse("Failed to save")
Templates is -
<h3>Album : {{album_name }}</h3>
{% for image in images %}
<img src="{{image.image.url}}" height="400" width="500">
<h4> {{image.image_name }}</h4>
<form method="POST" action=""> {% csrf_token %}
<span class = "badge">Description</span>
{% if image.description %}
<h4> {{image.description }} </h4>
{% else %}
<input type="text" value=" "/>
<button type="Submit">Submit</button>
{% endif %}
</form>
{% endfor %}
Where is your necessary name and id attributes for your input tag?
<input type="text" name="description" id="id_description"/>
Please try with {{ form.errors }} above "form" tag. And first of all check that what the errors arrive. Then Find the solution based on that error. Let me know if it is helpful or not.