The idea is to have something like this:
template.html
{% for item in items %}
<input type="checkbox" checked= {{ item.status }}>
{% endfor %}
views.py
def index(request):
context = { 'items' : Item.objects.all() }
return render(request, 'template.html', context)
But the status by design isn't simply True or False:
models.py
class Item(models.Model):
class Status(models.TextChoices):
ON = 1
OFF = 0
status = models.IntegerField(choices=Status.choices)
# other attributes..
How will I link these two values so that they're two-way connected? (Loading template.html yields the checkbox's checked-ness based on the retrieved item.status (checked for ON, unchecked for OFF, while checking or unchecking the checkboxes will change the related item.status value?)
The only thing I've seen closest to my problem is this, but it's not the same at all. Mine is a single binary attribute but has different kinds of values.
First of all, it seems that your status should be a BooleanField... But let's assume you really need those choices.
You need to tell your form to use a CheckboxInput instead of a Select.
You need to tell the widget which value should check the widget.
You need to convert the returned boolean as an Iteam.Status attribute.
class Form(forms.ModelForm):
class Meta:
model = Item
fields = ('status',)
widgets = {
'status': forms.CheckboxInput(
check_test=lambda status: status == Item.Status.ON,
),
}
def clean_status(self):
return (
Item.Status.ON if self.cleaned_data.get('status')
else Item.Status.OFF
)
Here are the part of the doc you need:
Override default fields
CheckboxInput
Cleaning specific field
Related
I'm currently just learning Django and I'm doing electronic grade book. I have tried everything, have read all the documentation, but nothing helps. It seems I miss a simple logic somewhere. I need to make two pages:
The first one "teacher_interface" is a simple inteface for the teacher with just one drop-down list, teacher chooses the necessary class (i.e 1C, 2B, 4C) and the button "Students", which should somehow take the chosen class from drop-down list input and redirect to the second page "class_students".
The second "class_students" is alike the "teacher_interface", but with the table of students of the chosen class.
I have the One-to-many relation between classes Student and Class:
Firstly, I tried redirecting from "teacher_interface" to "class_students", using in template:
{% url "name" %}
Parts of code: 1) models.py https://dpaste.org/eqxm 2) urls.py https://dpaste.org/eUEO 3) views.py https://dpaste.org/ap8D#L 4) template teacher_interface.html https://dpaste.org/v4m9 5) template class_students.html https://dpaste.org/0gXK
But it shows me: Reverse for 'class_students' with no arguments not found. 1 pattern(s) tried: ['school/teacher/(?P<class_id>[0-9]+)/class/$']
I tried everything, but nothing helped, this and the similar: Django - getting Error "Reverse for 'detail' with no arguments not found. 1 pattern(s) tried:" when using {% url "music:fav" %} I understood maybe this two options of redirect will not work in my case:
{% url 'class_students' class.id %}
{% url 'class_students' class_id %}
I also don't know if it's possible to do on the same page.
So I decided to redirect using redirect from django.shortcuts. I changed my teacher_interface view, so that it took the id of the chosen by the teacher class if request method is POST and redirected. I also made this change in my template "teacher_interface.html":
from
action="{% url 'class_students' %}"
to
action=""
Changed view:
def teacher_interface(request):
class_queryset = Class.objects.order_by("class_number", "group")
class_id = None
if request.method == "POST":
class_id = Class.objects.get("id")
return redirect("class_students", class_id)
context = {
"class_queryset": class_queryset,
"class_id": class_id,
}
return render(request, "teacher_interface.html", context)
But when I choose the class and click the "Students" button, it shows me: Cannot resolve keyword 'i' into field. Choices are: class_number, curriculum, discipline, group, id, student, task, type_of_class, type_of_class_id. Id is certainly is a key, but it tries to resolve only "i".
I tried/read everything here, but nothing works.
I even wrote the default like this:
class_id = Class.objects.get("id", "default")
I am sure I just don't understand properly how to get teacher's choice, pass it to another or the same function and redirect, saving this information. I will be really grateful for you help, even if you just advise what I can read to figure it out.
Ok, you are missing some basic conpects.
on your views.py
def teacher_interface(request):
class_queryset = Class.objects.order_by("class_number", "group")
context = {
"class_queryset": class_queryset,
}
return render(request, "teacher_interface.html", context)
this is correct, you will pass you query to your template
on your template change some things to look like this:
<form method="POST" >{% csrf_token %}
<select name="input1">
{% for class in class_queryset %}
<option value="{{ class.id }}">{{ class }}</option>
{% endfor %}
</select>
<input type="submit" value="Students"/>
</form>
then you need to change your teacher_interface view:
You need to import redirect on your views.py
def teacher_interface(request):
class_queryset = Class.objects.order_by("class_number", "group")
context = {
"class_queryset": class_queryset,
}
if request.method == 'POST':
class_id = request.POST.get('input1') # I'm not sure if this will get the {{class.id}} value, if don't, print(request.POST.get) and check how to get the value
return redirect('class_students', class_id=class_id) # will make a get request on the class_students view
return render(request, "teacher_interface.html", context)
def class_students(request, class_id):
# the parameter need to be 'class_id' because this is what you put on your urls '<int:class_id>', if possible, remove that /class.
# ADD CLASS ID AS PARAMETER, THAT WILL ENABLE YOU TO ACESS AN SPECIFIC CLASS
# Import get_object_or_404 (google it and you will find easily)
class = get_object_or_404(Class, pk=class_id) # this avoid internal server error.
# pass your class on the context
return render(request, "class_students.html")
I have this function inside my model that is not appearing when I try to run the server. I think I am accessing the method correctly but when I tried writing print("ENTER") inside the total_balance() function, nothing showed up which makes me think that it's not even entering the method at all. Oddly, the function works if I take out the search functionality.
model.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def total_balance():
transaction_list = Transaction.objects.filter(user=User)
total_balance_amount = 0
for transaction in transaction_list:
if transaction.category=='Income':
total_balance_amount += transaction.amount
elif transaction.category=='Expense':
total_balance_amount -= transaction.amount
return total_balance_amount
views.py
def profile(request):
if request.method == 'GET':
query = request.GET.get('q')
if query and query!="":
results = Transaction.objects.filter(Q(tag__icontains=query))
else:
results = Transaction.objects.all()
transactions = {
'transactions' : results,
}
profile = {
'profile' : Profile.objects.all()
}
return render(request, 'users/profile.html', transactions, profile)
template.py
<h5 class="card-title">Total Balance</h5>
<p class="card-text">₱{{ profile.total_balance }}</p>
Can someone please help me identify the reason this is not working and how I might be able to fix it? Thank you.
There are at least four things wrong here.
Firstly, for some reason you are passing two separate dictionaries to render. That doesn't work; you need a single dictionary with multiple entries.
context = {
'transactions' : results,
'profile' : Profile.objects.all()
}
return render(request, 'users/profile.html', context )
Secondly, profile - despite the singular name - is a queryset of all profiles. You would need to iterate through it in your template:
{% for prof in profile %}
<p class="card-text">₱{{ prof.total_balance }}</p>
{% endfor %}
Ideally, you would use a more appropriate name for the context variable, ie profiles.
Next, your total_balance method itself has two issues. Firstly, any method in Python needs to take the self parameter. And secondly, you need to use that parameter to access the value of the user field, not the class User. So:
def total_balance(self):
transaction_list = Transaction.objects.filter(user=self.user)
although note that that second line could be more easily written:
transaction_list = self.user.transaction_set.all()
Background:
I'm building a personal dictionary web-application, and have a queryset of terms and definitions. In my web app, I have an edit page, where I want to show a ModelFormSet that allows the user to edit any/all entries, and delete them if needed - the form has a delete button for each row and a submit button to save changes to the form.
The current issue is that when I click on a set to edit, the formset shows up correctly, and when I change a term and hit "Submit" the form updates to show the change. However, when I go back to my "View Set" page, the term hasn't been updated, which I assume means the change didn't go through to the actual database - the form itself was simply submitted. This is what I would like to currently fix, and I also want to make it so that the delete button deletes the entry.
What I've Tried:
I've gone through every StackOverflow question I could find relating to the topic, and various solutions were: add an instance parameter when passing in "request.POST", re-initialize the formset after saving, change the "action" url in the HTML page, etc., but every solution I try either results in another error or doesn't change anything.
I also checked the documentation but the examples provided were for simpler examples, and I'm not sure where exactly my error is in this case.
Finally, I also used {{ form.errors }} to see if the forms were being validated, and this resulted in an empty bracket and no issues, so I think I know for sure that the form is being validated, and the POST request is working.
Code:
MODELS.PY
class Set(models.Model):
title = models.CharField(max_length = 64, null = False, blank = False)
description = models.CharField(max_length = 255, null = False, blank = True)
def __str__(self):
return self.title
class Entry(models.Model):
set = models.ForeignKey(Set, on_delete=models.CASCADE)
term = models.TextField()
definition = models.TextField()
def __str__(self):
return self.term
FORMS.PY
from django.forms import ModelForm
from django.forms.models import modelformset_factory
from .models import Set, Entry
class SetForm(ModelForm): # Form that maps to Set
class Meta:
model = Set
fields = ['title', 'description']
class EntryForm(ModelForm):
class Meta:
model = Entry
fields = ['set', 'term', 'definition']
VIEWS.PY
def editEntry(request, set_id):
EntryFormSet = modelformset_factory(Entry, EntryForm, extra=0)
set_obj=Set.objects.get(id=set_id)
entry_list = set_obj.entry_set.order_by("term")
entry_formset=EntryFormSet(queryset=entry_list)
if request.method == 'POST':
instances=entry_formset.save()
for instance in instances:
instance.save()
entry_formset = EntryFormSet(queryset=instances)
else:
entry_formset = EntryFormSet(queryset=entry_list)#formset_factory(entry_form)
return render (request, 'dictTemplates/editEntries.html', {'entry_formset': entry_formset})
EDIT ENTRIES.HTML
<h1 style="text-align:center"><strong></center>Edit Entries Page</strong></h1>
<form method="POST" action = "">
{% csrf_token %}
{{ entry_formset.management_form }}
<center><table id="entriesFormSet" class="table">
<input type ="submit" value ="Submit Form">
<tr>
<th><h3>Terms</h3></th>
<th><h3>Definitions</h3></th>
</tr>
<tbody>
{% for form in entry_formset %}
<tr>
<td>{{ form.term }}</td>
<td>{{ form.definition }}</td>
<td class="delete-entry-button"><input type = "submit" value = "Delete Term"></td>
</tr>
{% endfor %}
</tbody>
</table></center>
</form>
URLS.PY
urlpatterns = [
path('', views.home, name='Home'),
path('about/', views.about, name='About'),
path('sets/', views.sets, name='Sets'),
path('sets/create/', views.createSet, name='createSet'),
path('sets/edit/(?P<set_id>[\d]+)', views.editSet, name='editSet'),
path('sets/delete/(?P<set_id>[\d]+)', views.deleteSet, name='deleteSet'),
path('sets/view/(?P<set_id>[\d]+)', views.viewSet, name='viewSet'),
path('entrys/create/(?P<set_id>[\d]+)', views.createEntry, name='createEntry'),
path('entrys/edit/(?P<set_id>[\d]+)', views.editEntry, name='editEntry'),
path('entrys/delete/(?P<entry_id>[\d]+)', views.deleteEntry, name='deleteEntry'),
]
The desired result is that clicking "Submit" results in an updated form plus changes in the database, but right now, clicking "Submit" only results in an updated form at that moment - clicking to another page or any other action makes the form revert to the original state.
I think the problem is that my Submit button somehow isn't "mapping" to saving the form into the database, that there's a missing connection there somewhere, but I'm not sure how to find it.
Please let me know if I should format/post this question differently, as it's my first SO question. Thank you so much!! Any help would be very much appreciated!
Not sure if something else is also wrong, but your view doesn't instantiate your formset well. Try with the following code:
EntryFormSet = modelformset_factory(Entry, EntryForm, extra=0)
if request.method == 'POST':
entry_formset=EntryFormSet(data=request.POST)
if entry_formset.is_valid():
entry_formset.save() # this will save all the instances
else:
set_obj=Set.objects.get(id=set_id)
entry_list = set_obj.entry_set.order_by("term")
entry_formset=EntryFormSet(queryset=entry_list)
return render(request, 'dictTemplates/editEntries.html', {'entry_formset': entry_formset})
If you want you may assign return value to formset save like instances = entry_formset.save() and debug which instances are saved (that returned value will be a list of those that are saved to database) by passing it to template together with formset in context and show the value in template.
I am currently beginning web development using django. In my application, I want a form with a varied number of questions and their choices to be presented.
In models.py, a table is create to store the questions
class QuizItems(models.Model):
question = models.CharField(max_length=255)
choices = SeparatedValuesField(token="$")
answer = models.IntegerField()
In form.py, I overload the __init__ method in Form class so as to pass qchoose, a list of QuizItems instances to create the form fields.
def choiceItem(question):
return [(unicode(idx), q) for idx, q in enumerate(question.choices)]
class QuizForm(forms.Form):
def __init__(self, qchoose, *args, **kwargs):
super(QuizForm, self).__init__(*args, **kwargs)
for q in qchoose:
self.fields[str(q.id)] = forms.ChoiceField(required=True,
label=q.question, widget=forms.RadioSelect(),choices=choiceItem(q))
Then in view.py
if request.method == 'POST':
idlst = request.POST.keys()
else:
# qchoose is a list of quizitems
form = QuizForm(qchoose)
In quiz.html
{% for field in form %}
<li><b> {{ field.label }} </b></li>
<ul> {{ field }} </ul>
{% endfor %}
I want to get idlst, the list of question id, that I can get the correct answers from. It works fine when all the choicefields are filled. The problem is if there is any choicefield value is empty, I won't get its key. I think since the request.POST is a dictionary, it is supposed to return all the keys even if its value is empty.
Could anyone help me what is wrong with my code or anything missing? Thank you!
You're supposed to use the form on POST as well, then call is_valid() and access the form's cleaned_data dict.
I have a template called client_details.html that displays user, note and datetime. Now sometimes, a client may not have an entry for user, note and datetime. What my program will do instead is display None if these fields are empty. I do not want the to display None. If a field has no value I don't want to see any value e.g. let it be blank if possible instead of displaying None.
views.py
#login_required
def get_client(request, client_id = 0):
client = None
try:
client = models.Client.objects.get(pk = client_id)
except:
pass
return render_to_response('client_details.html', {'client':client}, context_instance = RequestContext(request))
template
{{client.datetime}}<br/>
{{client.datetime.time}}<br/>
{{client.user}}<br/>
{{client.note}}<br/>
Use the built-in default_if_none filter.
{{ client.user|default_if_none:" " }}
{{ client.user|default_if_none:"" }}
you may use:
{% if client %} {{client.user}} {% else %} {% endif %}
Checking with an if is enough, so you may not user else block if you want...
this is such a strange problem.
I have a good idea for it. If you want to modify your field at display time than rather checking it at template , check it at your model class.
ExampleModel(models.Model):
myfield = models.CharField(blank=True, null = True)
#property
def get_myfield(self)
if self.myfield:
return self.myfield
else:
return ""
Use it in your template directly instead of field.
{{ExampleModel.get_myfield}}
you never need to change your template to change this field in future, just modify you property.