Using Dynamic Variable to populate Form label/verbose name - python

I am new to Django. I want to populate a form for model Score with dynamic labels/verbose names from another model : Question.
Essentially, each user has 5 current questions which they are going to score in a form as either "yes" or "no"
I have the data for a User's current five questions saved in a dictionary and can pass this dictionary into the view or template, but do not know how to use a dictionary to population the labels/ verbose names for the form.
#Model
class Score(models.Model):
yesno = ((0,"No"),(1,"Yes"),)
oneScore =models.IntegerField(choices=yesno,default='none')
twoScore =models.IntegerField(choices=yesno,default='none')
threeScore =models.IntegerField(choices=yesno,default='none')
fourScore =models.IntegerField(choices=yesno,default='none')
fiveScore =models.IntegerField(choices=yesno,default='none')
author = models.ForeignKey('auth.User')
date_created = models.DateTimeField(blank=False, null=False)
#Model
class QuestionManager(models.Manager):
def current_for_user(self,user):
question1 = Question.objects.filter(author=user,statementNumber=1).order_by('-id').first()
question2 = Question.objects.filter(author=user,statementNumber=2).order_by('-id').first()
question3 = Question.objects.filter(author=user,statementNumber=3).order_by('-id').first()
question4 = Question.objects.filter(author=user,statementNumber=4).order_by('-id').first()
question5 = Question.objects.filter(author=user,statementNumber=5).order_by('-id').first()
question_list = {"1":question1,
"2":question2,
"3":question3,
"4":question4,
"5":question5}
return question_list
class Question(models.Model):
statementNumber=models.IntegerField(choices=NUMBER, default='1',verbose_name="The number of the statement")
text = models.CharField(max_length=500,help_text="Enter your text", verbose_name="New Statement")
author = models.ForeignKey('auth.User')
date_created = models.DateTimeField(blank=False, null=False)
objects=QuestionManager()
#Form
class ScoreForm(forms.ModelForm):
class Meta:
model = Score
fields = ('oneScore','twoScore','threeScore','fourScore','fiveScore','bigScore')
#View
def score(request):
user = request.user
questions = Question.objects.current_for_user(user)
if request.method == "POST":
questions = Question.objects.current_for_user(user)
form = ScoreForm(request.POST)
if form.is_valid():
score = form.save(commit=False)
score.author = request.user
score.save()
return redirect('scoresheet',pk=score.pk)
else:
form = ScoreForm()
return render(request,'MC/score.html',{'form': form,'questions':sorted(questions.items())})

I'm not really sure what you're after, but you can pass anything you like to the form:
class ScoreForm(forms.ModelForm):
def __init__(self, questions, *args, **kwargs):
super().__init__(*args, **kwargs)
for num, question in questions.items():
self.fields.get('some_field').label = question
#View
def score(request):
user = request.user
questions = Question.objects.current_for_user(user)
if:
# ...
else:
form = ScoreForm(questions)
return render(request,'MC/score.html',{'form': form})

Related

Django order_by randomizes queryset twice?

I am trying to randomize a query set for a program i'm working on.
This is what's is in my views.py
full_quiz = Quiz.objects.get(name=quiz_name).questions.all()
form = TheForm(full_quiz)
if request.method == "POST":
form = QuestionForm(request.GET, full_quiz)
This is what is in my forms.py
class QuestionForm(forms.Form):
def __init__(self, questions, *args, **kwargs):
super(QuestionForm, self).__init__(*args, **kwargs)
for i in range(len(questions)):
if questions[i].answer.count() < 2:
self.fields["field_name %d" % i] = forms.CharField(label=questions[i], required=False)
From my testing, it appears like the query set is getting randomized with the second form variable. Any fix for this?
Thanks
You can get a random order queryset as described here:
full_quiz = Quiz.objects.get(name=quiz_name).questions.order_by('?')
You have not posted the code of your forms but I expect QuestionForm is a ModelFormSet. You can change the queryset of a ModelFormSet by setting the queryset arg (see docs):
form = QuestionForm(queryset=fullquiz)
Suppose you have a model structure like this
class Quiz(models.Model):
name = models.CharField('Quizname', max_length=200)
questions = models.ManyToManyField('Question')
class Answer(models.Model):
reply = models.CharField('Answer', max_length=100)
class Question(models.Model):
the_question = models.CharField('Question', max_length=100)
answers = models.ManyToManyField('Answer', related_name='answers')
correct_answer = models.ForeignKey('Answer', on_delete=models.CASCADE)
def is_correct(self, given_answer):
return given_answer == self.correct_answer
you could create a form structure with a ModelForm and a ModelFormSet:
class QuestionForm(forms.ModelForm):
myanswers = forms.ModelChoiceField(queryset=Answer.objects.all(), widget=forms.RadioSelect, required=False)
class Meta:
model = Question
fields = ('the_question', 'myanswers')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['myanswers'].queryset = self.instance.answers.all()
QuestionFormSet = modelformset_factory(Question, form=QuestionForm, extra=0)
And use it in a view:
def myview(request, quiz_name):
if request.method == 'POST':
formset = QuestionForm(request.POST)
if form.is_valid():
allanswers = [f['id'].is_correct(f['myanswers']) for f in formset.cleaned_data]
print('Number of correct answers:', sum(allanswers))
fullquiz = Quiz.objects.get(name=quiz_name).questions.order_by('?')
formset = QuestionForm(queryset=fullquiz)
return render(request, 'yourtemplate.html', {'formset': formset })

How to display a ModelChoiceField's Selected Value in Django

I need to display just the selected value from a forms.ModelChoiceField on a view page. How would I do that?
I've looked at many different forums and couldn't get a clear answer on what I should do in my case. I am new at Python.
form:
class Manufacturer1Form(ReadOnlyFormMixin, ModelForm):
manufacturer = forms.ModelChoiceField(queryset=Vendor.objects.filter(vendor_type='manufacturer').order_by('name'))
class Meta:
model = Manufacturer1Relationship
exclude = ('part',)
model:
class Manufacturer1Relationship(models.Model):
part = models.ForeignKey(Part, on_delete=models.CASCADE)
manufacturer = models.ForeignKey(Vendor, on_delete=models.CASCADE,
limit_choices_to={'vendor_type': 'manufacturer'},)
partNumber = models.CharField(max_length=40, blank=True)
class Meta:
permissions = (
('modify_admin_site', 'Can modify, add, view, or delete manufacturer relationships'),
)
view:
def PartView(request, type_id, id):
partType = Type.objects.get(id=type_id)
instance = get_object_or_404(Part, id=id)
selection = None
if request.method == 'POST':
form = ViewPartForm(type_id, request.POST, request.FILES, instance=instance)
manu1_formset = ManufacturerFormSet(request.POST, instance=instance)
location1_formset = LocationFormSet(request.POST, instance=instance)
if form.is_valid():
selection = form.cleaned_data['active_feed']
part = form.save(commit=False)
part.partType_id = type_id
if manu1_formset.is_valid() and location1_formset.is_valid():
part.save()
manu1_formset.save()
location1_formset.save()
url = reverse('list_parts', args=[partType.pk])
return HttpResponseRedirect(url)
else:
form = ViewPartForm(type_id=type_id, instance=instance)
manu1_formset = ManufacturerFormSet(instance=instance)
location1_formset = LocationFormSet(instance=instance)
return render(request, 'part_view.html', {'view_part_form': form,
'location_formset': location1_formset,
'manu_formset': manu1_formset,
'selection': selection,
'partType': partType,
'part': instance})
class Manufacturer1Form(ReadOnlyFormMixin, ModelForm):
manufacturer = forms.ModelChoiceField(queryset=Vendor.objects.filter(vendor_type='manufacturer').order_by('name'))
class Meta:
model = Manufacturer1Relationship
exclude = ('part',)
def __init__(self, *args, **kwargs):
initial_manufacturer = kwargs.pop("manufacturer",None)
super().__init__(*args, **kwargs)
self.fields["manufacturer"].initial = initial_manufacturer
ManufacturerFormSet(request.POST, instance=instance, manufacturer=specific_manufacturer)

Reference objects using foreign keys in Django forms

I did a lot of search for an issue I am facing, but couldn't find a suitable solution. I am a Django beginner
I am creating a project in which an User will be able to ask a wish, and other users will be assigned that wish, which they can then draw and submit.
I created views for asking and getting a wish, but facing issue while submitting the sketch for the wish. I do not know how to show only those wishes in the add_sketch form for the current user and then update the sketch model with this new sketch.
Right now I am just using a charField for the uploaded sketch. Here is the code
models.py
class Wish(models.Model):
content = models.CharField(max_length=500)
wisher = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add=True)
locked = models.BooleanField(default=False)
class Meta():
verbose_name_plural = 'Wishes'
def __unicode__(self):
return self.content
class Sketch(models.Model):
wish = models.ForeignKey(Wish)
sketcher = models.ForeignKey(User)
image_temp = models.CharField(max_length=128)
likes = models.IntegerField(default=0)
assigned_on = models.DateTimeField(auto_now_add=True)
submitted_on = models.DateTimeField(auto_now=True)
class Meta():
verbose_name_plural = 'Sketches'
def __unicode__(self):
return "Sketch for \""+ self.wish.content + "\""
views.py
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST)
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm()
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
And here is the forms.py
class GetWishForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Wish.objects.filter(pk__in = Wish.objects.filter(locked=False)[:3].values_list('pk')), initial=0)
class Meta:
model = Sketch
fields = ('wish',)
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
UPDATE:
I edited the code according to #almalki's suggestion
forms.py
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
def __init__(self, *args, **kwargs):
super(SketchForm, self).__init__(*args, **kwargs)
self.fields['wish'].queryset = kwargs.pop('wish_qs')
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
views.py
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST)
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm(wish_qs=Wish.objects.filter(wisher=request.user))
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
I still get the error init() got an unexpected keyword argument 'wish_qs'
UPDATE 2:
forms.py remains same as above, here is what I think the views.py should be
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST, wish_qs=Sketch.objects.filter(sketcher=request.user))
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm(wish_qs=Sketch.objects.filter(sketcher=request.user))
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
When I choose a wish, and click submit, the error is: annot assign "": "Sketch.wish" must be a "Wish" instance.
I know this is because the model is expecting a Wish instance, but we are giving a Sketch instance, but I don't know how to achieve what I need. I think some change has to be made in the models.py, connecting Wish and Sketch reversibly.
You need to override the field query set in form initialization:
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
def __init__(self, *args, **kwargs):
wish_qs = kwargs.pop('wish_qs')
super(SketchForm, self).__init__(*args, **kwargs)
self.fields['wish'].queryset = wish_qs
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
And in your view, you need to pass a queryset filtered based on current logged in user:
sketch_form = SketchForm(request.POST, wish_qs=Wish.objects.filter(wisher=request.user))
and:
sketch_form = SketchForm(wish_qs=Wish.objects.filter(wisher=request.user))

Only validate admin form if condition is true

I have a question model and a choices model. A choice can be correct or not.
class Choice(models.Model):
question = models.ForeignKey('Question', related_name='choices')
choice = models.CharField(max_length=255)
is_correct = models.BooleanField(default=False)
times_chosen = models.IntegerField(editable=False, default=0)
def __unicode__(self):
return self.choice + ' / ' + str(self.times_chosen)
#multiple choice question
class Question(models.Model):
def _get_average(self):
"Returns the average in percent"
if self.times_total == 0:
return 0.0
return (self.times_correct / float(self.times_total)) * 100
def _get_answer(self):
"Returns the answer"
for choice in self.choices.all():
if choice.question == self and choice.is_correct:
return choice.choice
return None
def __unicode__(self):
return self.question
question = models.CharField(max_length=255)
modules = models.ManyToManyField(Module, related_name='questions')
creator = models.CharField(max_length=255)
last_updated = models.DateTimeField(auto_now=True)
#used for global statistics per question
times_correct = models.IntegerField(editable=False, default=0)
times_total = models.IntegerField(editable=False, default=0)
#Derived values
average = property(_get_average)
answer = property(_get_answer)
First I tried only saving when there was an answer.
def save(self):
" Make sure that a question has at least one answer "
if self._get_answer():
super(Question, self).save()
But Question can't save because it has no answer set, and it can't have an answer set until its saved.
So I guess whenever I have a Question form I need to check if it has an answer before it is valid.
The form is in the admin and it uses inlines. So I created a new form class and would like to use it in the admin instead.
class ChoiceInline(admin.TabularInline):
model = Choice
extra = 4
#TODO: move?
class QuestionAdminForm(forms.ModelForm):
class Meta:
model = Question
def clean(self):
data = self.cleaned_data
logger.info(data)
data = self.cleaned_data['choices']
logger.info(data)
#if "fred#example.com" not in data:
# raise forms.ValidationError("You have forgotten about Fred!")
# Always return the cleaned data, whether you have changed it or
# not.
return data
class QuestionAdmin(admin.ModelAdmin):
readonly_fields = ('average', 'last_updated')
#list_display = ["question", "module", "average", "quiz"]
#can't have below because M2M question-> module
#list_display = ["question", "module", "average"]
list_display = ["question", "average"]
list_display_links = ["question"]
list_filter = ['modules__name']
search_fields = ["question", "modules__name", "quiz__name"]
inlines = [ChoiceInline]
actions = [duplicate_questions]
form = QuestionAdminForm
However self.cleaned_data doesn't contain choices. So I can't use that to validate if one of them is the answer.
EDIT
Here is the POST data
creator
u'Siecje'
choices-0-is_correct
u'on'
choices-1-choice
u'No'
choices-0-id
u''
choices-__prefix__-question
u''
choices-1-id
u''
question
u'Question Four?'
choices-0-question
u''
csrfmiddlewaretoken
u'hfRAW8B03as6XN5GpIygJ642VKMN2TPa'
choices-__prefix__-id
u''
choices-3-id
u''
_save
u'Save'
choices-2-question
u''
choices-2-id
u''
choices-MAX_NUM_FORMS
u'1000'
choices-INITIAL_FORMS
u'0'
choices-3-question
u''
choices-3-choice
u'So'
choices-0-choice
u'Yes'
choices-__prefix__-choice
u''
choices-1-question
u''
modules
u'24'
choices-2-choice
u'Maybe'
choices-TOTAL_FORMS
u'4'
This is what I ended up doing based on Django admin validation for inline form which rely on the total of a field between all forms
class CombinedFormSet(BaseInlineFormSet):
# Validate formset data here
def clean(self):
super(CombinedFormSet, self).clean()
for form in self.forms:
if not hasattr(form, 'cleaned_data'):
continue
data = self.cleaned_data
valid = False
for i in data:
if i != {}:
if i['is_correct']:
valid = True
if not valid:
#TODO: translate admin?
raise forms.ValidationError("A Question must have an answer.")
# Always return the cleaned data, whether you have changed it or
# not.
return data
class ChoiceInline(admin.TabularInline):
model = Choice
extra = 4
formset = CombinedFormSet

django complex formset issue

I have 3 primary models. Questionnaire model or question set contains set of questions. All user response are stored in answer.
Now I have to generate a formset which will store answer of all questions in a questionnaire set. How can I do it in django. So far I have manged to do it by displaying single question at once from a given questionnaire and store the response. My problem is that based on questiontype use two different modelform (MultipleChoiceAnswerForm,DescriptiveChoiceAnswerForm) and validate them based on the formtype. How can I use it in formset.
I am beginner in django and any help is appreciated.
My Code:
#Models.py
class Question(models.Model):
statement = models.CharField(max_length=255)
question_type = models.CharField(max_length=20, choices=get_qtypes())
remarks = models.CharField(max_length=200, null=True, blank=True)
def __unicode__(self):
return '%s'%(self.statement)
class Questionnaire(models.Model):
title = models.CharField(max_length=255)
questionaire_type = models.CharField(max_length=20,choices=QUESTIONNAIRETYPE)
context = models.ForeignKey(QuestionContext)
questions = models.ManyToManyField(Question)
timestamp = models.DateTimeField(auto_now=True)
tathya_user = models.ForeignKey(User)
def __unicode__(self):
return '%s'%(self.title)
class Answer(models.Model):
question = models.ForeignKey(Question)
person = models.ForeignKey(Person)
course = models.ForeignKey(Course)
teacher=models.ForeignKey(Person, null=True, blank=True, default = None)
questionaire = models.ForeignKey(Questionnaire)
statement = models.CharField(max_length=255)
def get_label(self):
return '%s'%(self.question.statement)
def get_choices(self):
return get_questionchoices(self.question.question_type)
class DescriptiveAnswerForm(ModelForm):
def __init__(self, *args, **kwargs):
super(DescriptiveAnswerForm, self).__init__(*args, **kwargs)
if kwargs.has_key('instance'):
self.fields['statement'].label = kwargs['instance'].get_label()
statement = forms.CharField(widget=forms.Textarea())
class Meta:
model = Answer
exclude=('question','person','course','teacher','questionaire')
class MultipleChoiceAnswerForm(ModelForm):
statement = forms.ChoiceField(widget=forms.RadioSelect(choices=EMPTY,attrs={'class': 'allradio',}))
def __init__(self, *args, **kwargs):
super(MultipleChoiceAnswerForm, self).__init__(*args, **kwargs)
if kwargs.has_key('instance'):
self.fields['statement'].label = kwargs['instance'].get_label()
self.fields['statement'].choices = kwargs['instance'].get_choices()
class Meta:
model = Answer
exclude=('question','person','course','teacher','questionaire')
###################################################################
#view.py
#login_required
def content_feedback_view_old(request,course_code):
#do validation and other jobs
questionnaire = get_questionnaire(some_params_like_coursecode)
if request.method == 'POST':
r_answer = Answer()
r_answer.question = Question.objects.get(id=request.session['question'])
r_answer.person = student
r_answer.course = course
r_answer.questionaire = questionnaire
r_answer.tathya_user = User.objects.get(id=request.user.pk)
rformtype = request.POST['formtype']
if rformtype == 'MCQ':
rform = MultipleChoiceAnswerForm(request.POST, instance=r_answer)
else:
rform = DescriptiveAnswerForm(request.POST, instance=r_answer)
if rform.is_valid():
rform.save()
else:
#return HttpResponse(printerror("Some problem occurred!"))
errortext = "You need to provide an input!"
questions = questionnaire.questions.all()
allquestions = questions.count()
tot_q = 0
formtype = ""
answered = 0
for question in questions:
try:
answer=Answer.objects.get(question=question,person=student,course=course,questionaire=questionnaire)
answered += 1
except:
answer = Answer()
answer.question = question
answer.person = student
answer.course = course
answer.questionaire = questionnaire
answer.tathya_user = User.objects.get(id=request.user.pk)
request.session['question']=question.id
tot_q = tot_q + 1;
if get_questiontype(question.question_type)=='MCQ':
formtype="MCQ"
form=MultipleChoiceAnswerForm(instance=answer)
else:
formtype="DESC"
form=DescriptiveAnswerForm(instance=answer)
break
if tot_q>0:
data_dict['FeedbackFormType']=formtype
data_dict['FeedbackForm']=form
data_dict['pagetitle']=context.description
data_dict['coursecode']=course.course_code
data_dict['feedbacktitle']="Content Feedback for "+course.fullname
data_dict['Completeness'] = (answered/allquestions)*100
data_dict['error']=errortext
else:
return HttpResponse(printerror("Thanks! You've answered all the questions!<br>Continue with the teaching feedback."))
req_context = RequestContext(request)
return render_to_response('view.html', data_dict, req_context)
Simple answer: only use on single AnswerForm and let it manage which kind of field and widget it should use, ie:
class AnswerForm(ModelForm):
def __init__(self, *args, **kwargs):
super(AnswerForm, self).__init__(*args, **kwargs)
instance = self.instance
if instance.question.question_type == 'MCQ':
self.fields["statement"] = forms.ChoiceField(
choices=instance.get_choices(),
widget=forms.RadioSelect(attrs={'class': 'allradio',})
)
else:
self.fields["statement"] = forms.CharField(
widget=forms.Textarea()
)
self.fields['statement'].label = instance.get_label()
class Meta:
model = Answer
exclude=('question','person','course','teacher','questionaire')
As a side note, you can pass model's attributes values to the model's constructor:
answer = Answer(
question=Question.objects.get(id=request.session['question']),
person=student,
course=course,
questionnaire=questionnaire,
# User.objects.get(id=request.user.pk) will return request.user
# so it's just useless - just use request.user
tathya_user=request.user
)

Categories

Resources