django - change list in POST request - html

I have a tuple list and wanted to delete or add tuples in it depending on what button has been pressed. Adding tubles is functioning fine but my problem is, that for some reason if Im clicking on the button to delete a tuple, the list resets back the time to the state before the delete happened.
For example I have a list:
ctestformat = [('sung', 4, 15), ('ren', 3, 27), ('lexe', 4, 39)]
after deleting the number 15 I get:
ctestformat = [('ren', 3, 27), ('lexe', 4, 39)]
But after getting another POST request to delete or add, the list resets to the first state as if nothing got deleted
Here is my view to add and delete tuple depending on which button was clicked:
def editorstart(request, ctestformat=[]):
if request.method == 'POST':
"""If clicked on create gap button, create a new gap and put it in ctestformat"""
if 'create_gap' in request.POST:
selectedgap = request.POST['sendgap']
startindex = int(request.POST['startpoint'])-13
ctestformat.append((selectedgap, len(selectedgap), startindex))
ctestformat.sort(key=operator.itemgetter(2))
"""if clicked on deletegap, delete the gap from ctestformat"""
elif 'deletegap' in request.POST:
deleteindex = request.POST['deletegap']
test = [t for t in ctestformat if t[2] != int(deleteindex)]
ctestformat = test
# This function doesnt change anything to ctestformat
modifiedtext = createmodifiedtext(ctestformat)
return render(request, 'editor_gapcreate.html', {"text": modifiedtext, 'ctestformat': ctestformat})
If you have any other questions, just ask :)
EDIT:
added return in my view
my template:
{% extends "base_generic2.html" %}
{% block content %}
<form action="editorgapcreate" id=create method="POST">
<input type="hidden" name="sendgap" id="sendgap">
<input type="hidden" name="startpoint" id="startpoint">
<script src="../static/textselector.js"></script>
<div id="thetext" onmouseup="getSelectionText()">
<h1>{{ text|safe }}</h1>
</div>
{% csrf_token %}
<p></p>
<b>Your current selected gap:</b>
<p id="currentgap"></p>
<input type="hidden" name="text" id="text" value="{{ text }}">
<button type="submit" name="create_gap" id="gapcreate">Create gap</button>
</form>
{% endblock %}

Using a mutable value for a default argument in Python (a list in this case) is not normally a good idea. The list is created once when the function is defined, which means any changes you make to it are visible in subsequent function invocations. However, it seems as though this may be intended in your case.
The reason why you're not seeing the list change, is that the assignment you're making ctestformat = test after filtering out an item has no effect. You need to mutate the original list rather than reassigning, by first finding the index of the item within that list, and then using pop() to remove it. For example:
elif 'deletegap' in request.POST:
deleteindex = request.POST['deletegap']
for i, t in enumerate(ctestformat):
if t[2] == int(deleteindex):
ctestformat.pop(i) # Modify original list
break
...
I would still recommend not using a mutable default argument to achieve this. If you need to share data across requests, you'd be better to use a cache or a database, or possibly session state, depending upon your application requirements.

Related

How Do I Properly Submit Two Forms Within One HTML Structure?

I am trying to submit a create view with two forms. The code below works fine if everything is filled out and the form submitted. However if fields are omitted in form2...the form submission fails and the field that was filled out for "form"..."name"....gets reset. I've read you can do multiple forms and I've largely got this working...I just need to figure out how to incorporate form2 into the if_valid().... Here's my view...
def tasklist_detail_view(request, id):
context = {}
context["tasklist"] = TaskList.objects.get(id=id)
context["tasks"] = Task.objects.filter(task_list=id).all()
obj = get_object_or_404(TaskList, id=id)
form = UpdateTaskListForm(request.POST or None, instance=obj)
form2 = TaskForm(request.POST or None)
context["task_list_id"] = id
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse("MyTaskLists:my_task_list_main_menu"))
context["form"] = form
context["form2"] = form2
return render(request, "my_task_list_tasklist_detail.html", context)
My HTML...
<form method="POST" enctype="multipart/form-data" id="forms">
{% csrf_token %}
{{ form.name }}
{% include "my_task_list_task_create_form1.html" with tasklist=tasklist %}
<button type="submit" class="button66" name="status" value="Submitted">Submit</button>
</form>
And then in my include HTML...
<div id="task-list-form" hx-target="this" hx-swap="outerHTML">
<button class="button35" hx-post="{% url 'MyTaskLists:task-create' id=task_list_id %}">Save</button>
{{ form2 }}
I did try to do something like....
if form.is_valid() and form2.is_valid():
form.save()
return HttpResponseRedirect(reverse("MyTaskLists:my_task_list_main_menu"))
But then nothing happens...the forms are not accepted at all even if the fields are filled out properly....From what I've read I understand the POST is being applied to both forms....if one is not filled out properly that is why the other errors out? I just can't quite figure out how to process them both properly.
Thanks in advance for any thoughts.
If you want the two forms to behave like one form, and save two objects only if both forms are valid, then the logic is
if form.is_valid() and form2.is_valid():
form.save()
form2.save()
return HttpResponseRedirect(reverse("MyTaskLists:my_task_list_main_menu"))
context["form"] = form
context["form2"] = form2
return render(request, "my_task_list_tasklist_detail.html", context)
If there is any field with the same name in form and form2 you need to use a prefix to remove the ambiguity.
form = UpdateTaskListForm(request.POST or None, instance=obj, prefix='form1')
form2 = TaskForm(request.POST or None, prefix='form2')

Django How to make the condition correct on an HTML page - search bar

I try to do a search engine if the word in my DB thah I created then display the word on the HTML page and if not then nothing.. I did it right in VIEW but I can not apply it on the HTML page I searched the internet and did not find an answer I'm sure I fall for something stupid.
This is the view
def Search_word(request):
search = request.POST.get("search") #Grab the search item
return render(request,"search_page.html", {"search":search})
this is the html:
{%for i in Word.English_word%}
{%if search in Word.English_word%}
{{search}}
{%endif%}
{%endfor%}
and the urls:
path("Search_page",views.Search_word ,name="Search-page"),
models:
class Words(models.Model):
English_word = models.CharField(max_length=30)
Hebrew_word = models.CharField(max_length=30)
How_To_Remember = models.CharField(max_length=40)
Name = models.CharField(max_length=20)
pub_date = models.DateTimeField()
The problem is that even if the word is valid it does not show me anything ..
You should implement the filtering logic in the view, not in the template. Templates are for rendering logic, not business logic. Furthermore one should filter with the database, since databases are designed to do this.
The view thus looks like:
def Search_word(request):
search = request.POST.get('search')
items = Word.objects.filter(English_word__contains=search)
return render(
request,
'search_page.html',
{'search': search, 'items': items}
)
and then in the template we render this with:
{% for item in items %}
{{ item.English_word }}: {{ item.Hebrew_word }} <br>
{% endfor %}
You can use as lookup __contains to check if the English_word has a substring that is equal to search, with __icontains you check case-insensitive, with __iexact you look for Words that match search case-insensitive, and finally you can filter with Engish_word=search for an exact match.

How do I access a single field while using For Loop to iterate through all the fields of a ModelForm in Django Template?

I have a model which has four ForeignKey fields, so they are dropdown fields in the form.
class Package(models.Model):
patient=models.ForeignKey(Patient, on_delete=CASCADE)
diagnosis=models.ForeignKey(Diagnosis, on_delete=CASCADE)
treatment=models.ForeignKey(Treatment, on_delete=CASCADE)
patient_type=models.ForeignKey(PatientType, on_delete=CASCADE)
date_of_admission=models.DateField(default=None)
max_fractions=models.IntegerField(default=None)
total_package=models.DecimalField(max_digits=10, decimal_places=2)
The forms.py:
class PackageForm(ModelForm):
class Meta:
model=Package
fields='__all__'
widgets={
"patient_type" : forms.Select(attrs={"onblur":"mf();"}),
"max_fractions" : forms.NumberInput(attrs={"onfocus":"mf();", "onblur":"tp();"}),
"total_package" : forms.NumberInput(attrs={"onfocus":"tp();", "onblur":"onLoad();"}),
'date_of_admission': DateInput(attrs={'type': 'date'}),
The views.py:
def package_view(request):
if request.method=='POST':
fm_package=PackageForm(request.POST, prefix='package_form')
if fm_package.is_valid():
package=fm_package.save()
IpdReport.objects.create(patient=package.patient, package=package)
fm_package=PackageForm(prefix='package_form')
return render (request, 'account/package.html', {'form5':fm_package})
else:
fm_package=PackageForm(prefix='package_form')
return render (request, 'account/package.html', {'form5':fm_package})
The Template:
<form action="" method="post" novalidate>
{% csrf_token %}
{{form5.non_field_errors}}
{% for fm in form5 %}
<div>
{{fm.label_tag}}
{{fm}}
<span>{{fm.errors|striptags}}</span><br><br>
</div>
{% endfor %}
<button type="submit" id="savebtn">Save</button>
</form>
Now, what I want is to insert an Anchor Tag next to all the foreign_key fields, in the template, to add a new object into the original table. For example, an Add Patient option next to the Patient's dropdown field, when clicked, a new, small window would show up with Patient form. The user enters the new patient's data, saves it and the same name shows up in the dropdown.
But as I am using a For Loop in the template, how would I be able to access those foreign key fields and apply the options? Any suggestions, please?
If it isn't a problem I would move away from rendering all of the fields with the 'forloop'. Instead I would use notation: form.field to render different fields. So it would look like:
{{ form.patient.label_tag }}
{{ form.patient }}
It should be much easier to navigate through fields this way, but of course it will require more typing :)

How to determine which button is pressed

I have a table in which I populate with data from the database. Some of this I have an extra feature of the delete button. But I can't understand how I get that what delete button is pressed in django
<tbody>
{% for i in list %}
<tr>
<td>{{i.id}}</td>
<td>{{i.reason}}</td>
<td>{{i.starting_date}}</td>
<td>{{i.ending_date}}</td>
<td>{{i.accept_or_not}}</td>
{% if i.accept_or_not == 'pending'%}
<td><input type="button" name="data_delete_{{i.id}}" value="delete"></td>
{%endif%}
</tr>
{% endfor%}
</tbody>
def profile_view(request):
if 'data_delete_id' in request.POST:
# i don't know how to determine the id in the button
This might be straight-forward. You can get the name of the button first and then parse the id such as:
def profile_view(request):
delete_button_id = ""
for name in request.POST.values():
if name.startswith('data_delete_'):
delete_button_id = int(name.split('_')[2])
break
# you now have the id in your delete_button_id variable
According to Django Docs for HttpRequest.POST your python code has to work as expected because POST returns a dict like Object.
def profile_view(request):
if 'data_delete_id' in request.POST:
But in your template you are using <input type="button" .
This will only trigger a client side operation. If you are not having any JS code which you have not shown here, I would say you need to use <input type="submit" instead.
I would also recommend to use <button type="submit" instead of input because from a semantic perspective it is more accurate.

Django: combine items from two queries (two different Models) together

I'm building an event platform with Django with some events public and some invite-only. Here's how it basically works:
Every event is based on the Event model.
Private events are only viewable by invited guests: this is achieved
through an Invite model referencing both the event and the guest.
Users can inform whether they will be attending an event or not
through an Answer model that stores the associated event, user
and the answer.
The Answer and Invite models are totally independent from each other, that way I am able to use the Answer model for both public and private events.
What I'm trying to achieve:
For each event where I'm invited, display the invitation (event.creator invited you to event.name) and, if it exists, my associated answer, else display a form to answer.
So I think what I'm trying to do is getting all events where I'm invited (I have no problem with that) and joining that to my answer (where user=me). The problem is that the answer might not even exist yet --if I haven't answered.
Ideally, I would have all of that in one single query so that I could do something like this in the templates: invited_event.answer and display the answer if it exists.
EDIT:
So I think what I need ultimately is to mix two queries: one that gets all the events where I'm invited (1) and an other that grabs all answers for those events (2).
(1)
latest_events_invited = Event.objects.filter(invite__user_invited=request.user)
(2)
answers_events_invited = Answer.objects.filter(user_answering=request.user, event__in=latest_events_invited)
Something like: for each event in latest_events_invited, append corresponding answer from answers_events_invited.
Any ideas?
Code:
My template (index.html):
<h3>Invites</h3>
{% if latest_invites_list %}
<ul>
{% for event in latest_events_invited %}
<li>
{{ event.creator }} invited you to {{ event }}<br/ >
<!--IDEALLY:-->
{% if event.answer %}
You answered: {{ answer.answer }}
{% else %}
<form action="{% url 'events:answer_event' invite.event.id %}" method="post">
{% csrf_token %}
Answer:
<select name="answer">
<option value="1" >Attending</option>
<option value="2" >Not attending</option>
</select>
<input type="submit" name="submit" value="Answer">
</form>
{% endif %}
</li>
</ul>
{% else %}
<p>No invites.</p>
{% endif %}
The view (views.py)
def index(request):
if request.user.is_authenticated():
latest_events_invited = Event.objects.filter(invite__user_invited=request.user)
latest_answers_list = Answer.objects.filter(user_answering=request.user, event__in=latest_events_invited)
#do something with those to get: "latest_events_invited_with_answers"
context = {'latest_events_invited':latest_events_invited, 'latest_answers_list':latest_answers_list}
else:
[...]
return render(request, 'events/index.html', context)
And the models.
Event
class Event(models.Model):
PRIVACY_CHOICES = (
(0, 'Public'),
(1, 'Invite only'),
)
name = models.CharField(max_length=200)
[...]
privacy = models.PositiveSmallIntegerField(max_length=1, choices=PRIVACY_CHOICES, default=0)
invited = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Invite', related_name='events_invited', blank=True)
answers = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Answer', related_name='events_answered', blank=True)
Invite
class Invite(models.Model):
user_invited = models.ForeignKey(settings.AUTH_USER_MODEL)
event = models.ForeignKey(Event)
created = models.DateTimeField(default=timezone.now, editable=False)
class Meta:
unique_together = (("user_invited", "event"),)
Answer
class Answer(models.Model):
ANSWER_CHOICES = (
(1, 'Attending'),
(2, 'Not attending'),
)
user_answering = models.ForeignKey(settings.AUTH_USER_MODEL)
event = models.ForeignKey(Event)
answer = models.PositiveSmallIntegerField(max_length=1, choices=ANSWER_CHOICES)
class Meta:
unique_together = (("user_answering", "event"),)
Hopefully someone here can help me out.
Thanks.
One option is to automatically add an Answer whenever someone is invited, with a default of No. Use the post_save signal of Invite or overwrite the save method so you don't have to create an associated Answer each time.
Another option, probably better, is to do the logic in the view rather than in the template. Check if an answer exists; if it does, pass a list of answers to the template; if not, pass an empty list.
EDIT: Or, better yet:
In the view:
try:
user_answer = Answer.objects.filter(user_answering = request.user).filter(event=event)
except ObjectDoesNotExist:
answer = None
EDIT2: Or how about:
Answer.objects.filter(user_answering = request.user).filter(event__in=Event.objects.filter(user__in = event.invited))
Ok, so I figured it out. I don't know if this is the optimal solution but it works for now.
In the end, it is only 2 queries, which I combine with for loops.
Here's the code: (views.py)
def index(request):
if request.user.is_authenticated():
latest_events_invited = Event.objects.filter(invite__user_invited=request.user)
latest_answers_for_events = Answer.objects.filter(user_answering=request.user, event__in=latest_events_invited)
for event in latest_events_invited:
for answer in latest_answers_for_events:
if answer.event.id == event.id:
event.answer = answer.answer
else:
event.answer = ''
context = {'latest_events_invited': latest_events_invited,}
return render(request, 'events/index.html', context)
I can then access the answer directly in the template (index.html) like so:
{{ event.answer }}