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
Related
So I have a problem about how to save model instance with foreign key relation,
models.py
class Connect(models.Model):
username = models.CharField(max_length=255)
password = models.CharField(max_length=255,null=True, blank=True)
conft = models.TextField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return unicode(self.username)
class Ip(models.Model):
class Meta:
db_table = 'autonet_ip'
connect_id = models.ForeignKey(Connect, on_delete=models.CASCADE)
ipaddr = models.CharField(max_length=255)
def __str__ (self):
return self.ipaddr
forms.py
class NacmForm(ModelForm):
password = forms.CharField(widget=forms.PasswordInput,required = False)
class Meta:
model = Connect
fields = ['username', 'password','conft']
labels = {'conft':_('Config'),}
class IpForm(ModelForm):
class Meta:
model = Ip
fields = ['ipaddr']
labels = {'ipaddr':_('IP address'),}
IpFormset = formset_factory(IpForm, extra=1)
views.py
def konfig(request):
ip_list = []
status = ''
value_bak = 1
if request.method == 'POST':
formm = NacmForm(request.POST or None)
ipform = IpFormset(request.POST)
upform = UploadForm(request.POST,request.FILES)
userValue = formm['username'].value()
passValue = formm['password'].value()
confValue = formm['conft'].value()
usernamef = get_object_or_404(Connect, pk=id)
if ipform.is_valid():
for form in ipform:
ipaddr = form.cleaned_data.get('ipaddr')
//.... some code ...//
simpanIp = form.save(commit=False)
simpanIp.connect_id = usernamef
simpanIp.save()
simpanForm.save()
return HttpResponseRedirect('/konfig')
else:
formm = NacmForm()
ipform = IpFormset()
return render(request, 'konfig.html', {'form': formm, 'logins': Connect.objects.all(), 'ipform': ipform, 'status': status })
Then, when I input all data and click submit to collect form data and on simpanIp.save(), I've got an error: id() takes exactly one argument (0 given).
I just want to know how to save the instance of Connect model to database with foreign key, thanks in advance
so i edit my models.py like this
class Connect(models.Model):
......
def get_usernameinf(self):
return ', '.join(self.usernameinf.all().values_list('username', flat=True))
and views.py like this
if request.method == 'POST':
.....some code.....
if ipform.is_valid() and formm.is_valid():
simpanForm = formm.save()
for form in ipform:
simpanIp = form.save(commit=False)
...... some code ..
simpanIp.connect_id = simpanForm
simpanIp.save()
and its work, now the result is my "connect_id" got value from "Connect id"
id is a Python builtin that gives a unique ID for an object. I would guess that you did not intend to pass it get_object_or_404 on this line:
get_object_or_404(Connect, pk=id)
The calling convention for this functions seems to be that it is meant to be an integer for the primary key in a database table. Figure out where you should be getting your primary key from and set it correctly.
Pro-tip: avoid using names that are predefined by Python (see here for a full list). It can lead to headaches like the one you just had.
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})
This is my current Admin Interface:
A user inputs text through a form & model called "UserText". I have written a function using NLP to extract only the questions from the UserText. I would like each of these individual questions to be displayed in each "User question" section of the Admin Interface. As of now, I cannot get that to work.
Here is my current code:
Models.py
class UserText(models.Model):
user_input = models.TextField()
class Question(models.Model):
user_text = models.ForeignKey(
UserText,
on_delete=models.CASCADE,
blank=True,
null=True,
)
user_questions = models.CharField(max_length=2000)
Views.py
def user_text_view(request):
form = forms.UserTextForm()
if request.method == 'POST':
form = forms.UserTextForm(request.POST)
if form.is_valid():
UserText = models.UserText
Question = models.Question
user_input = request.POST.get('user_input', '')
user_input_obj = UserText(user_input = user_input)
user_questions_obj = Question(user_text = user_input_obj,
user_questions = Question_Init(user_input_obj))
user_input_obj.save()
user_questions_obj.save()
print("Thanks for the questions!")
else:
form = forms.UserTextForm()
return render(request, 'text_input_form.html', {'form': form})
Admin.py
class QuestionInLine(admin.StackedInline):
model = Question
display = ('user_questions_obj')
#admin.register(UserText)
class UserTextAdmin(admin.ModelAdmin):
model = UserText
display = ('user_input')
inlines = [
QuestionInLine,
]
And finally my function:
def Question_Init(user_input_obj):
Beginning_Question_Prompts = ("Who","Whom","What","Where","When","Why","Which",
"Whose","How","Was","Were","Did","Do","Does","Is")
Ending_Question_Prompts = ("?",":","...")
questions = []
text1 = user_input_obj.user_input
textList = sent_tokenize(text1)
for sentence in textList:
if sentence.startswith(Beginning_Question_Prompts):
questions.append(sentence)
if sentence.endswith(Ending_Question_Prompts):
questions.append(sentence)
return questions
I know this is a lot, sorry, but I do not know how to get each question to populate the question fields in my Admin Interface. Thanks
The problem is not your admin interface, but how you create the Question objects. You need to iterate through the result of your function and create linked items for each one:
for question_text in Question_Init(user_input_obj):
user_questions_obj = Question(user_text=user_input_obj,
user_questions=question_text)
user_questions_obj.save()
I am just starting to learn about Django and I am running into an issue. I have a modelFormSet that is meant to add 3 choices to a question you create. I have no issue when I am not using the modelFormset and only adding one question but when I try to iterate through a modelsFormset and assign each choice to the question that was just created I get the following error:
NOT NULL constraint failed: polls_choice.question_id
I think it has something to do with the question_id not being passed to the choice model but I am not sure how to fix it. I have run fresh migrations and I don't think I can set blank or null to True since I need the choice and question to be related. Thank you in advance for your help!
Models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
assigned_to = models.ManyToManyField(User)
def __str__(self):
return self.question_text
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
class Choice(models.Model):
question = models.ForeignKey(Question)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
Forms
class CreateQuestion(forms.ModelForm):
class Meta:
model = Question
fields = ('question_text', 'assigned_to', 'pub_date',)
class AddChoices(forms.ModelForm):
class Meta:
model = Choice
fields = ('choice_text',)
View
def create_question(request):
choices_formset = modelformset_factory(Choice, form=AddChoices, fields=('choice_text',), extra=3)
if request.method == 'POST':
question_form = CreateQuestion(data=request.POST)
choice_form = choices_formset(request.POST, request.FILES)
if question_form.is_valid and choice_form.is_valid:
question = question_form.save()
for choice in choice_form:
choice.question = question
choice.save()
return HttpResponseRedirect(reverse('polls:index'))
else:
return render(request, 'polls/createquestion.html', {'question_form': question_form,
'choice_form': choice_form, })
else:
question_form = CreateQuestion()
choice_form = choices_formset(queryset=Choice.objects.none(),)
return render(request, 'polls/createquestion.html', {'question_form': question_form,
'choice_form': choice_form, })
When you loop through for choice in choice_form, each item is a form, so setting the question attribute doesn't work.
Instead, you should save with commit=False, set the question, then save the object to the db.
for form in choice_form:
choice = form.save(commit=False)
choice.question = question
choice.save()
Problem is here:
for choice in choice_form:
choice.question = question
choice.save()
You're iterating here over AddChoices forms, not over Choice objects. That mean, you're saving question as an attribute of form, not as attribute of model instance and that won't propagate into model instance.
To fix it you can try:
for form in choice_form:
choice = form.save(commit=False)
choice.question = question
choice.save()
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
)