I need to open the same form multiple times while looping trough some items.
This is the form:
class CancelRefundForm(forms.forms.Form):
cancel = forms.BooleanField(label='Check to cancel the refund', required=True ,widget=forms.CheckboxInput(attrs={
'class': 'hidden-checkbox',
'name': 'hiddenCheckBoxName'
}
))
item = forms.IntegerField(
required=True,
widget=forms.NumberInput(attrs={
'class': 'cancel-item-input',
'value': '',
'type': 'hidden'
}),
)
This is the HTML:
<div class="order-items-display">
{% for item in order.orderitem_set.all %}
<div class="single-order-item">
<span >
<a href="{{ item.item.get_absolute_url }}">
<img src="../../../media/{{ item.item.main_image }}" alt="{{ item.item.title }} image">
</a>
</span>
<span class="order-item-details">
<h3><a href="{{ item.item.get_absolute_url }}">
{{ item.item.title }}
{% if item.replica %}
<small>(Replica)</small>
{% endif %}
</a></h3>
<p><strong>Price: </strong>{{ item.old_price }}€</p>
{% if item.quantity > 1 %}
<p><strong>Quantity: </strong>{{ item.quantity }}</p>
<p><strong>Total: </strong>{{ item.get_old_total_price }}€</p>
{% endif %}
{% if order.refund_deadline > nowDate %}
<p><b>The deadline for requesting a refund ends in {{ order.refund_deadline }}(30 days)</b></p>
{% else %}
<p><b>The deadline for requesting a refund ended in {{ order.refund_deadline }}.</b></p>
{% endif %}
<button class="myBtnPri update-cart" data-product="{{item.id}}" data-action="add">
<a class="mySecLink" href="#">
Buy Again
</a>
</button>
{% if item.refund_requested %}
<p><b>Refund requested: </b> {{ item.refund_requested }}</p>
<p><b>Refund granted: </b> {{ item.refund_granted }}</p>
<form action="{% url 'order-details' order.slug %}" method="POST" class="cancel-form">
{% csrf_token %}
{{ cancelForm.item }}
<label for="{{ cancelForm.cancel.id_for_label }}" class="myCheckbox">
{{ cancelForm.cancel }}
<div class="checkbox-box"></div>
<b>Check to cancel the refund</b>
</label>
<button class="myBtnPri cancel-btns" type="submit" data-item="{{ item.item.id }}">
<a class="mySecLink">
Cancel Refund
</a>
</button>
</form>
{% elif item.refund_requested == False and order.refund_deadline > nowDate %}
<button class="myBtnPri refundBtn" data-item="{{ item.item.id }}" data-qty="{{ item.quantity }}">
<a class="mySecLink" href="#">
Refund?
</a>
</button>
{% else %}
<p>Since the refund deadline is over you can no longer refund this Iitem.</p>
{% endif %}
</span>
</div>
{% endfor %}
</div>
In this html I display the items ordered by the user from an old order he made where the user can choose to refund a specific item. If the the user asks for a refund the template will then display a form that asks if the user wants to cancel the refund.
If the user decides to refund more than one item from the same order I will have to display the cancelForm one time for each item.
In my views I have:
def order_details_view(request, slug):
dataCart = cartData(request)
cancelForm = CancelRefundForm()
# toRefundItems = len(OrderItem.objects.filter(order = order, refund_requested = True))
# cancelRefundFormSet = formset_factory(CancelRefundForm, extra=toRefundItems)
# formset = cancelRefundFormSet()
order.refund_deadline = order.refund_deadline.astimezone(timezone.utc).replace(tzinfo=None)
if request.method == 'POST':
cancelForm = CancelRefundForm(request.POST)
# formset = cancelRefundFormSet(request.POST)
# CANCEL REFUND FORM LOGIC
if cancelForm.is_valid():
itemId = cancelForm.cleaned_data['item']
orderItem = OrderItem.objects.get(order = order, item = itemId)
if orderItem.refund_requested == True:
orderItem.refund_requested = not cancelForm.cleaned_data['cancel']
orderItem.save()
refund = RefundedItems.objects.get(order = order, item = orderItem)
refund.delete()
order.save()
messages.success(request, f'You successfully canceled the refund of the item {orderItem.id}.')
return redirect(f'/order_details/{order.slug}/')
context = {
'order': order,
'cancelForm': cancelForm,
# 'cancelForm': formset,
'nowDate': datetime.datetime.now(),
'cartItems': dataCart['cartItems'],
}
return render(request, 'users/order_details.html', context)
As you can see by the comments I tried to use formset_factory, but it did not work
because I needed to open more than one form.
And the way I have above its also not working properly. The multiple forms are displayed in the template but when I check the box from any form it just updates the first one.
Should I create another view and template to deal with this using formset's? Or am I missing something?
Thanks
To anyone that has the same question, I decided to make one form for each item I needed using Javascript then I displayed it in the template and connected the form to a new view that only handles the cancel refund form's information.
Related
I have a Django form in which I use crispy-forms along with bootstrap5. Everything was alright until I wanted to changed the Layout of my form. As my form is constructed dynamically, I just wanted to define the value of a set element item which is under the form (field_name, layout_index). The goal was to define it as a FieldWithButton, as I couldn't find another way to do that.
To do that, I modified my helper in the __init__ method of my form :
self.helper[item[1]] = Div(FieldWithButtons(item[0], StrictButton("Add item")), id=f'div_id_{item[0]}', css_class='mb-3 row')
This is rendered nearly correctly in my form, I have the FieldWithButton with Div etc. However, the div which contains my FieldWithButton doesn't take the field_class of my helper that I defined, and instead creates a <divclass='col-10'>...</divclass='col-10'>.
There's juste a space which disappeared and messed everything up. How can I either remove the class='col-10' part of my div and put it as its class or differently define my Field as a FieldWithButton ?
Here's my whole form class if needed :
class ScriptInputForm(forms.Form):
def __init__(self, *args, **kwargs):
variables = kwargs.pop('variables') # All variables to render
variables_names = [*variables] # under the form {'name':['type', 'description']}
super().__init__(*args, **kwargs)
for var in variables_names: # Dynamic creation of the fields
values = variables[var]
field = self.fields[var] = forms.CharField()
field.widget.attrs['placeholder'] = values[1].title()
self.helper = FormHelper(self)
num = 1 # Rough part where I define the tuples ('name', 'index') of all lists in my variables
lists = []
for k,v in variables.items():
if v[0]=='list':
lists.append((k,num))
num+=1
for item in lists: # Part where the problem is coming from
self.helper[item[1]] = Div(FieldWithButtons(item[0], StrictButton("Add item")))
self.helper.add_input(Submit('submit', 'Submit'),)
self.helper.label_class = 'col-2'
self.helper.field_class = 'col-10'
self.helper.form_action = reverse_lazy('scripts:data_input')
And the rendered HTML :
<div>
<div class="mb-3 row">
<label for="id_liste" class="col-form-label col-2">Liste</label>
<divclass="col-10"> <!-- With <div class="col-10"> everything's ok -->
<div class="input-group">
<input type="text" name="liste" placeholder="Your List" class="textinput textInput form-control" id="id_liste">
<button class="btn" type="button">Add item</button>
</div>
</divclass="col-10">
</div>
</div>
Seems like it was an error in crispy-bootstrap5.
The FieldWithButtons display is defined in field_with_buttons.html whose code is the following :
<div{% if div.css_id %} id="{{ div.css_id }}"{% endif %} class="mb-3{% if 'form-horizontal' in form_class %} row{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}{% if div.css_class %} {{ div.css_class }}{% endif %}" {{ div.flat_attrs }}>
{% if field.label and form_show_labels %}
<label for="{{ field.id_for_label }}" class="{% if 'form-horizontal' in form_class %}col-form-label {% else %}form-label {% endif %}{{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</label>
{% endif %}
<div{% if field_class %}class="{{ field_class }}"{% endif %}> <!-- Here -->
<div class="input-group{% if input_size %} {{ input_size }}{% endif %}">
{% if field.errors %}
{% crispy_field field 'class' 'form-control is-invalid' %}
{% else %}
{% crispy_field field 'class' 'form-control' %}
{% endif %}
{{ buttons|safe }}
</div>
{% for error in field.errors %}
<p id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="text-danger mb-0"><small><strong>{{ error }}</strong></small></p>
{% endfor %}
{% include 'bootstrap5/layout/help_text.html' %}
</div>
</div>
Just had to add a space at the start of the second div to fix the issue.
I have a Django method written to update user profile. The purpose of the method is solved, as I am able to click on the "update" button and modify the existing data.
Note: I have written method to update Default User model and extended User model(custom fields). Below is the code snippet from views
views.py
#login_required(login_url="/login/")
def editUserProfile(request):
if request.method == "POST":
form = UserProfileUpdateForm(request.POST, instance=request.user) # default user profile update
obj = UserProfile.objects.get(user__id=request.user.id)
form1 = UserProfileForm(request.POST or None, instance=obj)
if form.is_valid() and form1.is_valid():
obj.Photo = form1.cleaned_data['Photo']
obj.dob = form1.cleaned_data['dob']
obj.country = form1.cleaned_data['country']
obj.State = form1.cleaned_data['State']
obj.District = form1.cleaned_data['District']
obj.phone = form1.cleaned_data['phone']
form.save()
form1.save()
messages.success(request, f'updated successfully')
return redirect('/profile1')
else:
messages.error(request, f'Please correct the error below.')
else:
form = UserProfileUpdateForm(instance=request.user)
form1 = UserProfileUpdateForm(instance=request.user)
return render(request, "authenticate\\editProfilePage.html", {'form': form, 'form1': form1})
corresponding HTML code.
editProfilePage.html
{% load static %}
{% block content %}
<h2 class="text-center">Edit Profile</h2>
<form method="POST" action="{% url 'editUserProfile' %}">
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-warning alert-dismissable" role="alert">
<button class="close" data-dismiss="alert">
<small><sup>x</sup></small>
</button>
<p>Form has error..!!</p>
{% for field in form %}
{% if field.errors %}
{{ field.errors }}
{% endif %}
{% endfor %}
</div>
{% endif %}
{{ form.as_p }}
{{ form1.as_p }}
<!-- {{ form1.dob }}-->
<!-- {{ form1.country }}-->
<!-- {{ form1.State }}-->
<!-- {{ form1.District }}-->
<!-- {{ form1.phone }}-->
<input type="submit" value="update" class="btn btn-secondry">
</form>
<br/><br/>
{% endblock %}
If one see in the first glance I do not see any issue immediately as my purpose of updating the profile is successful.
However, to test, after I update a user profile I logout the user which redirects me to login page, and there I see error "Please correct the error below." three times which is coming from the "else" part of the update method. I also see a message "updated successfully" on the same login screen which is coming from the "if" part of the update method as mentioned above(screenshot attached -- update_error3).
So, I have below observations:
My update method "editUserProfile" is somehow calling the inner most "if - else" together.
I think the issue lies in the HTML page. I could say this because when I click on the "update" button from the profile page, I see "email" field appearing twice on the screen and the "update button to insert the data to database(screenshot attached -- update_error1).
Now, I could see the rest of the fields only after I click on the "update" button further(screenshot attached -- update_error2). Furthermore, this is when I enter all my details and when I click on the "update" button, the data get saved successfully in the database and redirected to the profile page with the new data.
Finally, I think the issue is something related to the HTML code. May be I am wrong.
Any thought?
update_error1
The problem is with your messages rendering on the template.Just add this block of code
to your base template that immediately extends your update template.
{% if messages %}
<ul class="messages ">
{% for message in messages %}
<ol class="breadcrumb ">
<li{% if message.tags %} class="{{ message.tags }} " {% endif %}><strong>{{ message }} {{form.errors}}</strong>
</li>
</ol>
{% endfor %}
</ul>
{% endif %}
I would like to write a one-template view, which depending on the url path will show different field of the model. So for instance if path is
http://127.0.0.1:8000/trip/2/1/
I will get second trip from my db (that works), and 1 should give (as html is written) a description field. I don't know how to process this to context_processor in my view. Do you have any ideas?
views.py
class TripDescriptionCreate(LoginRequiredMixin, UpdateView):
model = Trip
template_name = 'tripplanner/trip_arguments.html'
fields = ["description", "city", "country", "climate", "currency", "food", "cost", "comment", "accomodation",
"car", "flight", "visa", "insurance", "remarks"]
context_object_name = 'trips'
success_url = '/'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
trip_arguments.html
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{{ trips.tripName }}</legend>
{% if field_id == 1 %}
{{ form.description|as_crispy_field }}
{% elif field_id == 2 %}
{{ form.city|as_crispy_field }}
{% endif %}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Update</button>
</div>
</form>
urls.py
path('trip/<int:pk>/<int:field_id>', TripDescriptionCreate.as_view(), name='trip-fullfill'),
So I came up with this idea. In my html I added these lines:
{% url 'trip-fullfill-description' pk=trip.pk as description_url %}
{% url 'trip-fullfill-city' pk=trip.pk as city_url %}
{% if request.get_full_path == description_url %}
{{ form.description|as_crispy_field }}
{% elif request.get_full_path == city_url %}
{{ form.city|as_crispy_field }}
I just tried to add paginator for my search results page but it didn't work. I have 10 articles in search results and I set 3 for each page, which means totally the results list will be divided into 4 pages. However, when I searched keywords, it showed all 10 articles in the same page and the paginator didn't work.
My view.py is:
def search_titles(request):
q = request.GET.get('q')
error_msg = ''
if not q:
error_msg = 'Please enter keywords!'
return render(request, 'search_results.html', {'error_msg': error_msg})
else:
nq = q.split(' ')
a_list = Q(article_ti__icontains=nq[0]) | Q(article_content__icontains=nq[0]) | Q(
abstract__icontains=nq[0]) | Q(
author__icontains=nq[0])
for i in nq[1:]:
a_list.add(Q(article_ti__icontains=i) | Q(article_content__icontains=i) | Q(abstract__icontains=i) | Q(author__icontains=i), a_list.connector)
queryset = Article.objects.filter(a_list).distinct()
queryset_count = queryset.count()
paginator = Paginator(queryset, 3)
page_var = 'page'
page = request.GET.get(page_var, 1)
try:
sets = paginator.page(page)
except PageNotAnInteger:
sets = paginator.page(1)
except EmptyPage:
sets = paginator.page(paginator.num_pages)
return render(request, 'search_results.html', {'error_msg': error_msg, 'b_list': queryset, 'a_list_count': queryset_count, 'sets': sets, 'page_var':page_var})
my HTML code is:
<div class="ui text container">
{% if error_msg %}
<p>{{ error_msg }}</p>
{% else %}
{% for a in b_list %}
<div class="ui segment">
<a target="_blank" href={{ a.get_abs_url }}>
<h3>{{ a.article_ti }}</h3>
<h5>{{ a.author }}</h5>
<p>{{ a.abstract }}</p>
</a>
</div>
{% empty %}
<div class="ui segment">No results for '{{ request.GET.q }}'</div>
{% endfor %}
{% endif %}
</div>
<div class="pagination" id="m">
<span class="step-links">
{% if sets.has_previous %}
<<
<
{% endif %}
<span class="current"><input name="enter_page" value="{{ sets.number }}" id="page_num"> of {{ sets.paginator.num_pages }}</span>
{% if sets.has_next %}
>
>>
{% endif %}
</span>
</div>
I tried several methods on stackoverflow but still no work. Could anyone give me some suggestions?
I'll be very appreciated if you can help me!
In your template your are looping over 'b_list',
{% for a in b_list %}
<div class="ui segment">
<a target="_blank" href={{ a.get_abs_url }}>
<h3>{{ a.article_ti }}</h3>
<h5>{{ a.author }}</h5>
<p>{{ a.abstract }}</p>
</a>
</div>
{% empty %}
<div class="ui segment">No results for '{{ request.GET.q }}'</div>
{% endfor %}
change this to,
{% for a in sets %}
<div class="ui segment">
<a target="_blank" href={{ a.get_abs_url }}>
<h3>{{ a.article_ti }}</h3>
<h5>{{ a.author }}</h5>
<p>{{ a.abstract }}</p>
</a>
</div>
{% empty %}
<div class="ui segment">No results for '{{ request.GET.q }}'</div>
{% endfor %}
I want to display elements from bottom to top like how the facebook messages are displayed from bottom to top. How would I do that?
html:
<div id="message_wrapper">
<div class="message">
<p>1 Message message message....</p>
</div>
<div class="message">
<p>2 Message message message....</p>
</div>
<div class="message">
<p>3 Message message message....</p>
</div>
<div class="message">
<p>4 Message message message....</p>
</div>
</div>
So I would like it to display:
4 Message message message....
3 Message message message....
2 Message message message....
1 Message message message....
Update: Added django tempate
This the template for coversation.html:
<div id="messages_wrapper">
<!-- if there are conversation in the recent thread, then show the conversation -->
{% if conversations != 0 %}
<p><b>You have conversations:</b></p>
<!-- check the messagestate of each message of the recent_tread, if all the messages of the thread are not hidden then show the messages -->
{% if recent_thread|all_message_hidden:user %}
<p>All messages are hidden</p>
{% else %}
{% for conversation in conversations %}
{% if conversation|conversation_hidden:user %}
{% if conversation.sender == user %}
<div id="conversation" class="user_sent_conversation">
<p>{{conversation.id}}-{{conversation.body}} : Sender- {{conversation.sender}}, ID {{conversation.sender.id}}</p>
<p>Already hidden</p>
</div>
<hr/>
{% else %}
<div id="conversation" class="others_sent_conversation">
<p>{{conversation.id}}-{{conversation.body}} : Sender- {{conversation.sender}}, ID {{conversation.sender.id}}</p>
<p>Already hidden</p>
</div>
<hr/>
{% endif %}
{% else %}
{% if conversation.sender == user %}
<div id="conversation" class="user_sent_conversation">
<p>{{conversation.id}}-{{conversation.body}} : Sender- {{conversation.sender}}, ID {{conversation.sender.id}}</p>
[X]
</div>
<hr/>
{% else %}
<div id="conversation" class="others_sent_conversation">
<p>{{conversation.id}}-{{conversation.body}} : Sender- {{conversation.sender}}, ID {{conversation.sender.id}}</p>
[X]
</div>
<hr/>
{% endif %}
{% endif %}
{% endfor %}
<div id="message_form_wrapper_inside">
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="/inbox/send_messages/{{recent_thread|get_user_id:user}}/" method="post">
{% csrf_token %}
<label for="recipient">To - {{recent_thread|get_user_id:user}}</label>
<label for="subject">
Subject -
{% if recent_thread.subject != '' %}
{{recent_thread.subject }}
{% else %}
No subject
{% endif %}
</label>
<label for="body">Message</label>
<textarea name="body" id="body" value=""></textarea>
<input type="submit" value="Send">
</form>
</div>
{% endif %}
{% else %}
<p>No message for this thread</p>
{% endif %}
</div>
Updated: Added the views.py
Views for displaying the message.
#login_required(login_url='/accounts/required_login/')
def message(request):
user = request.user
# Get all the threads of that user
threads = user.thread_set.all()
# Order the threads by which thread recieved the latest message
order_threads_message = threads.annotate(max_sent_date=Max('message__sent_date')).order_by('-max_sent_date')
if order_threads_message.count() > 0:
# Get the recent thread
recent_thread = order_threads_message[0]
if recent_thread.message_set.all().count() > 0:
# Get the conversations of the recent thread
recent_thread_conversations = recent_thread.message_set.all()
return render(request, 'conversations.html', {
'recent_thread':recent_thread,
'all_threads':order_threads_message,
'conversations':recent_thread_conversations,
'active': recent_thread.id
})
else:
recent_thread_conversations = 0
return render(request, 'conversations.html', {
'recent_thread':recent_thread,
'all_threads':order_threads_message,
'conversations':recent_thread_conversations,
'active': recent_thread.id
})
else:
order_threads_message = 0
recent_thread_conversations = 0
return render(request, 'conversations.html', {
'all_threads':order_threads_message,
'conversations':recent_thread_conversations,
})
The simplest solution is to loop over the messages in reversed orderr:
{% for conversation in conversations reversed %}
...
{% endfor %}
In order to get it displayed "bottoms up" use the #reverse() method on your QuerySet.
This is assuming that the threads are sorted in descending order, that is, newest message is first.
recent_thread_conversations.reverse()