Django Rendering template objects error - django

I have a problem with the way I structured my function.
My function renders 2 forms. A form to change the primary picture for a whiteboard and a form to delete a picture from a particular whiteboard
Both forms displays a dropbox that list all whiteboards for the students to pick and when the students pick a whiteboard . it's displays all the objects.
The form that set a primary picture for a whiteboard works perfectly because it display all picture objects but when I choose a value from the dropbox under the delete picture.
The function doesn't return all the pictures objects underneath the delete picture header but it instead displays all the picture under the primary forms.
I think the problem is with my if forms.is_valid(): and my if formss.is_valid(): because when a form is submitted . It only get POST into the if forms.is_valid():
my views.py
def WhiteBoardEditor(request):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:LoginRequest'))
picture = {}
pict = {}
if request.method == "POST":
forms = BoardPictureForm(request.user,request.POST,)
formss = PictureDeleteForm(request.user,request.POST,)
if forms.is_valid():
board = forms.cleaned_data['board']
if board:
boards = forms.cleaned_data['board']
picture = Picture.objects.filter(board=boards)
return render(request,'boardeditor.html',{'picture':picture,'boardpicture':BoardPictureForm(request.user),'picturedelete':PictureDeleteForm(request.user)})
if formss.is_valid():
pooh = formss.cleaned_data['board']
if pooh:
pooh = formss.cleaned_data['board']
pict = Picture.objects.filter(board=pooh)
return render(request,'boardeditor.html',{'pict':pict,'boardpicture':BoardPictureForm(request.user),'picturedelete':PictureDeleteForm(request.user)})
return render(request,'boardeditor.html',{'boardpicture':BoardPictureForm(request.user),'picturedelete':PictureDeleteForm(request.user)})
my boardeditor.html
<h1> Set a primary picture for a whiteboard</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ boardpicture.as_p }}
<input type = "submit" value= "save" />
</form>
{% for p in picture %}
<li>{{p.description}}
{% endfor %}
<h1> Delete picture from whiteboard</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ picturedelete.as_p }}
<input type = "submit" value= "save" />
</form>
</form>
{% for pi in pict %}
{ pi.description }}
{% endfor %}
my forms.py
class BoardPictureForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super(BoardPictureForm, self).__init__(*args, **kwargs)
self.fields['board'].queryset = Board.objects.filter(user=user)
class Meta:
model = Picture
fields = ('board',)
class PictureDeleteForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super(PictureDeleteForm, self).__init__(*args, **kwargs)
self.fields['board'].queryset = Board.objects.filter(user=user)
class Meta:
model = Picture
fields = ('board',)

Your two forms use the same field which is the board. That's why when you submit the second form, the first form is the one process.
To fix your problem, you need to specify the action in every form. Notice that in my answer, I add input in hidden format with the process value. And in your view, I create if and else statement for that process so that when you submit the form the system will know which form must be executed.
if request.method == "POST":
forms = BoardPictureForm(request.user,request.POST,)
formss = PictureDeleteForm(request.user,request.POST,)
if request.POST['process'] == 'primary':
if forms.is_valid():
board = forms.cleaned_data['board']
if board:
boards = forms.cleaned_data['board']
picture = Picture.objects.filter(board=boards)
return render(request,'boardeditor.html',{
'picture':picture,
'boardpicture':BoardPictureForm(request.user),
'picturedelete':PictureDeleteForm(request.user)
})
elif request.POST['process'] == 'delete':
if formss.is_valid():
pooh = formss.cleaned_data['board']
if pooh:
pooh = formss.cleaned_data['board']
pict = Picture.objects.filter(board=pooh)
return render(request,'boardeditor.html',{
'pict':pict,
'boardpicture':BoardPictureForm(request.user),
'picturedelete':PictureDeleteForm(request.user
)}
elif request.POST['process'] == 'third':
//other form here
<h1> Set a primary picture for a whiteboard</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ boardpicture.as_p }}
<input type = "hidden" name="process" value= "primary" />
<input type = "submit" value= "save" />
</form>
{% for p in picture %}
<li>{{p.description}}
{% endfor %}
<h1> Delete picture from whiteboard</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ picturedelete.as_p }}
<input type = "hidden" name="process" value= "delete" />
<input type = "submit" value= "save" />
</form>
</form>
{% for pi in pict %}
{ pi.description }}
{% endfor %}

Edit2: Ah, I've misunderstood this question - the following isn't relevant to the question, but still useful to the OP
I suspect it's because your not generating well-formed HTML. Try changing the "picture" part in boardeditor.html to be
<ul>
{% for p in picture %}
<li>{{p.description}}</li>
{% endfor %}
</ul>
Edit: also,
{% for pi in pict %}
{{ pi.description }} <!-- added an opening curly brace -->
{% endfor %}
And you have a redundant </form> towards the bottom

Related

django - choices from __init__ are not loading in the form

I am using __init__ to build my form choices from parameters passed from the view. It looks like my choices are built correctly when I do print(choices), but the form is not loading any choices. There isn't even a widget for it showing. I do not get any errors. I've used similar code in other views which worked, which is one reason why this one is really confusing me.
I did see that print("ok") never gets printed to the shell, while print("else") does get printed
view
def newobjtoassess(request, assess_pk):
user = request.user
assessment = Assessment.objects.get(pk=assess_pk)
course_pk = assessment.course.pk
context['assessment'] = assessment
form = ObjToAssessmentForm(user=user, course_pk=course_pk)
if request.method == 'POST':
print("ok")
form = ObjToAssessmentForm(request.POST, user=user, course_pk=course_pk)
if form.is_valid():
f = form.cleaned_data
objective = f.get('objective')
assessment.objectives.add(objective)
assessment.save()
return HttpResponseRedirect(reverse('gradebook:assessupdate', args=[assess_pk]))
else:
context['form'] = form
return render(request, "gradebook/newobjtoassess.html", context)
else:
print("else")
form = ObjToAssessmentForm(user=user, course_pk=course_pk)
return render(request, "gradebook/newobjtoassess.html", context)
form
class ObjToAssessmentForm(forms.Form):
objective = forms.ChoiceField(label='Learning Objective', choices=[])
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
my_course = kwargs.pop('course_pk')
super(ObjToAssessmentForm, self).__init__(*args, **kwargs)
choices=[(o.id, str(o)) for o in Objective.objects.filter(user=user, course=my_course)]
print(choices)
self.fields['objective'] = forms.ChoiceField(choices=choices)
template
{% extends 'base-g.html' %} {% load static %} {% block content %}
{% load crispy_forms_tags %}
<div class="container">
<div class="row">
<div class="col">
<p>Course: {{ assessment.course }}</p>
</div>
</div>
<div class="row">
<div class="col">
<p>{{ assessment.assessment_name }}</p>
</div>
</div>
<div class="row">
<div class="col">
<form method="post">{% csrf_token %}
{{ form|crispy }}
<input type = "submit" value="Update">
</form>
</div>
</div>
</div>
{% endblock content %}
I got mixed up with the indentation on the else. As well, I needed to pass the form to the template in the else.
# the last (2nd of the two) else in the view above
else:
form = ObjToAssessmentForm(user=user, course_pk=course_pk)
context['form'] = form
return render(request, "gradebook/newobjtoassess.html", context)
The view can be cleaned up a bit, but this solves the problem.

How do I use two lists in one Django for tag?

I have two letters that I would like to show in my template at the same time. How can I do it in the template the fastest and easiest way?
Where set_1 = A, B, C, D,
ser_2 = result from my django queryset
Is there something like the below? Any help will be appreciated
{% for b in set_1 and a in set_2 %}
<p>{{ b }} - {{ a }}</p>
{% endfor %}
EDIT:
How can I use this when my queryset returns a list of fields in my form, as in the view below?
views.py
def account(request):
data_now = datetime.datetime.now().strftime("%Y-%m-%d")
test = Time.objects.filter(day_time__day_name='Monday')
#my form
TimeFormSet = modelformset_factory(Time, fields=('free_or_no',), labels={'free_or_no': '*odznacz jeżeli zajęte',})
if request.method == "POST":
formset = TimeFormSet(
request.POST,
queryset=Time.objects.filter(day_time__day_name='Monday'),
)
if formset.is_valid():
formset.save()
return HttpResponseRedirect(reverse('app:account'))
else:
formset = TimeFormSet(queryset=Time.objects.filter(day_time__day_name='Monday'))
list_form = zip(formset, test)
context = {'data_now': data_now, 'time_edit_form': formset, 'test': test, 'list_form': list_form}
return render(request, 'account.html', context)
If I do everything as in your link, my browser returns an error:
IntegrityError at /account/
NOT NULL constraint failed: app_time.time_equivalent
html file
<form action="." method="post">
{% csrf_token %}
{{ time_edit_form.management_form }}
{% for item1, item2 in list_form %}
<p>{{item2}} {{item1}}</p>
{% endfor %}
<button type="submit" class="btn btn-block btn-primary"> ZapiszXXX</button>
</form>

Django file now showing in the template

My file is being uploaded in the correct path but I have an issues with it:
Whenever I refresh my HTML page the file gets uploaded again and again. How do I solve this? Also
Please help me with the code or suggest me.
Thanks in advance:)
My views.py
def about_experiment(request, ex_link_name):
researcher = None
study = None
posts = None
exp = get_object_or_404(Experiment,link_name = ex_link_name)
high_scores = ScoreItem.objects.filter(experiment=exp,active=True)
context = {
'request': request,
'exp':exp,
'high_scores': high_scores,
'awards':AwardItem.objects.filter(experiment=exp,visible=True),
}
if exp.about_file:
context['about_file'] = settings.EXPERIMENT_DIRS+exp.about_file.get_include_path()
return render(request, 'about_experiment.html', context)
if request.method == 'POST':
form = AboutHelp(request.POST, request.FILES)
posts = Help.objects.filter().order_by('-date')[0]
documents = Help.objects.all()
if form.is_valid():
obj = form.save(commit = False)
obj.save()
researcher = form.cleaned_data['researcher']
study = form.cleaned_data['study']
document = form.cleaned_data['document']
else:
form = AboutHelp()
posts = Help.objects.filter().order_by('-date')[0]
documents = Help.objects.all()
return render(request, 'about_experiment.html', {'posts': posts})
return render(request, 'about_experiment.html', {'posts': posts})
Source page
<form action="{% url 'lazer.views.about_experiment' exp.link_name %}" method="POST" name="form" enctype="multipart/form-data">
{% csrf_token %}
<label>Researcher Name(s):
<input type="text" name="researcher"><br>
<lable>Study Summary
<textarea rows="10" cols="50" placeholder="Start typing..." maxlength="500" class="form-control" name="study"></textarea>
<br>
<label>Upload your IRB approval letter:
<input type ="file" id="irb-file" class="file_input" name="document"></label>
<br>
<input type = "submit" value="Submit" class="btn btn-primary" />
</form>
destination page
<div class="tab-pane" id="irb">
<h4> List of file(s) uploaded:</h4>
<!--File upload-->
{% if documents %}
<ul>
{% for file in documents %}
<li> {{ file.document.name }} </li>
{% endfor %}
</ul>
{% else %}
<p>No such documents available.</p>
{% endif %}
<!--File upload ends-->
</div>
{% if high_scores %}
{% for hs in high_scores %}
<div class="tab-pane" id="{{ hs.link_name }}">
{% high_score request exp.link_name hs.link_name %}
</div>
{% endfor %}
{% endif %}
As #almost a beginner pointed out, you should be redirecting to some other view, if your form is submitted successfully. If not, (in your case), when the page reloads, the code for POST request is executed again. ie, your form is submitted again. I could suggest some changes in your view,
def about_experiment(request, ex_link_name):
exp = get_object_or_404(Experiment,link_name = ex_link_name)
high_scores = ScoreItem.objects.filter(experiment=exp,active=True)
context = {
'request': request,
'exp':exp,
'high_scores': high_scores,
'awards':AwardItem.objects.filter(experiment=exp,visible=True),
'posts':Help.objects.filter().order_by('-date')[0],
'documents':Help.objects.all()
}
if exp.about_file:
context['about_file'] = settings.EXPERIMENT_DIRS+exp.about_file.get_include_path()
if request.method == 'POST':
form = AboutHelp(request.POST, request.FILES)
if form.is_valid():
obj = form.save(commit = False)
obj.save()
return redirect(reverse('lazer.views.about_experiment', kwargs={ 'ex_link_name':obj.link_name }))
else:
form = AboutHelp()
return render(request, 'about_experiment.html', context)
Here, I merely assumed your obj has a field link_name. You may need to change that according to your models.

Django: ModelFormSet saving first entry only

Update:
The issue seemed to be in the coding for Django-formset. I was processing it as an inline formset and not a model formset. The answer below was also correct. Thanks!
I am working with a model formset for an intermediate model. I am using django-formset js to add additional formset fields on the template. Most everything works OK except that when I go to save the formset only the first entry is being saved to the DB. The first entry is saved and assigned correctly but any after than just disappear. It is not throwing any errors so I am not sure what is going wrong. Thanks!
The Model
class StaffAssignment(models.Model):
study = models.ForeignKey(Study, related_name='study_set', null=True, on_delete=models.CASCADE)
staff = models.ForeignKey('account.UserProfile', related_name='assigned_to_set', null=True, on_delete=models.CASCADE)
role = models.CharField(max_length=100, null=True)
assigned_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-role',)
def __str__(self):
return '{} is assigned to {}'.format(self.staff, self.study)
The Form:
class AddStaff(forms.ModelForm):
model = StaffAssignment
fields = ('staff',)
def __init__(self, *args, **kwargs):
super(AddStaff, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({'class': 'form-control'})
The View:
def add_staff(request, study_slug):
study = get_object_or_404(Study, slug=study_slug)
staff_formset = modelformset_factory(StaffAssignment, form=AddStaff, fields=('staff',), can_delete=True)
if request.method == 'POST':
staffList = staff_formset(request.POST, request.FILES)
if staffList.is_valid():
for assignment in staffList:
assigned = assignment.save(commit=False)
assigned.study = study
assigned.role = assigned.staff.job_title
assigned.save()
return HttpResponseRedirect(reverse('studies:studydashboard'))
else:
HttpResponse('Something is messed up')
else:
staffList = staff_formset(queryset=StaffAssignment.objects.none())
return render(request, 'studies/addstaff.html', {'staffList': staffList, 'study': study})
The Template:
<form action="{% url 'studies:addstaff' study.slug %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="box-body">
{% for list in staffList %}
<div class="form-group" id="formset">
{% if list.instance.pk %}{{ list.DELETE }}{% endif %}
{{ list.staff }}
{% if list.staff.errors %}
{% for error in list.staff.errors %}
{{ error|escape }}
{% endfor %}
{% endif %}
</div>
{% endfor %}
{{ staffList.management_form }}
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
You are not including the primary key field in the template, as required by the docs. Add
{% for list in staffList %}
{{ list.pk }}
...
{% endfor %}

Retrieving models from form with ModelMultipleChoiceField

I am having difficulties with forms, specifically ModelMultipleChoiceField.
I've pieced together this code from various examples, but it sadly doesn't work.
I would like to be able to:
Search for some Works on work_search.html
Display the results of the search, with checkboxes next to each result
Select the Works I want, via the checkboxes
After pressing Add, display which works were selected.
I believe everything is okay except the last part. The page simply displays "works" :(
Here is the code - sorry about the length.
Models.py
class Work(models.Model):
title = models.CharField(max_length=200)
artist = models.CharField(max_length=200)
writers = models.CharField(max_length=200)
def __unicode__(self):
return self.title + ' - ' + self.artist
forms.py
class WorkSelectForm(forms.Form):
def __init__(self, queryset, *args, **kwargs):
super(WorkSelectForm, self).__init__(*args, **kwargs)
self.fields['works'] = forms.ModelMultipleChoiceField(queryset=queryset, widget=forms.CheckboxSelectMultiple())
views.py
def work_search(request):
query = request.GET.get('q', '')
if query:
qset = (
Q(title__icontains=query) |
Q(artist__icontains=query) |
Q(writers__icontains=query)
)
results = Work.objects.filter(qset).distinct()
form = WorkSelectForm(results)
return render_to_response("work_search.html", {"form": form, "query": query })
else:
results = []
return render_to_response("work_search.html", {"query": query })
def add_works(request):
#if request.method == POST:
form = WorkSelectForm(request.POST)
#if form.isvalid():
items = form.fields['works'].queryset
return render_to_response("add_works.html", {"items":items})
work_search.html
{% extends "base.html" %}
{% block content %}
<h1>Search</h1>
<form action="." method="GET">
<label for="q">Search: </label>
<input type="text" name="q" value="{{ query|escape }}">
<input type="submit" value="Search">
</form>
{% if query %}
<h2>Results for "{{ query|escape }}":</h2>
<form action="add_works" method="post">
<ul>
{% if form %}
{{ form.as_ul }}
{% endif %}
</ul>
<input type="submit" value="Add">
</form>
{% endif %}
{% endblock %}
add_works.html
{% extends "base.html" %}
{% block content %}
{% if items %}
{% for item in items %}
{{ item }}
{% endfor %}
{% else %}
<p>Nothing selected</p>
{% endif %}
{% endblock %}
In add_works, you're not constructing your WorkSelectForm the right way. It's expecting as a first parameter the queryset of possible/authorized choices, then the POST data.
Also, you're not accessing the selected works correctly from the form. You have to use is_valid method on the form, then use cleaned_data as described in the doc.
From what I see in your work_search view, there's no restriction on which Work objects you can search then add to the result, so you could do simply:
def add_works(request):
#if request.method == POST:
form = WorkSelectForm(Work.objects.all(), request.POST)
if form.is_valid():
# the items are in form.cleaned_data['works']
items = form.cleaned_data['works']
return render_to_response("add_works.html", {"items":items})
else:
# handle error case here
...