I have a base template which contains header, footer and a block "content" which then I override in different CBVs.
There is little "user-space" divinside a header where I want to keep user's info in case he is logged in and a login form if he is a guest.
Right now the only way that comes in mind is to create a django app and use it as a tag. But I think there is a better solution. Because as far as I know tags can slow down django project in future.
Also I think that maybe I would like to manipulate this div from a view which renders a child template. For example I calculate and display some value on a page but also I want to display it in a "user-space" div as well. I think this can be achieved by pushing data to the user's session and then using this while rendering "user-space" div from another code.
Assuming you have django.contrib.auth.user in your INSTALLED_APPS, you can access the user's login status using user.is_authenticated():
{% if user.is_authenticated %}
<div>Welcome back, {{ user.username }} | <a href='/logout/'>Logout</a></div>
{% else %}
<div>
<form action='/login/' method='POST'>
{{ csrf_token }}
{{ login_form }}
<input type='submit'>
</form>
</div>
{% endif %}
Edit:
In response to your comment:
Let's suppose client does a POST request by which we calculate some number - total price. And I need to display it in that div.
As documented
Define your view:
from django.shortcuts import render
from .forms import MyForm
def simple_view(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = MyForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
result = form.cleaned_data['some_input'] * 50
return render(request, 'my_template.html', {'result': result})
# if a GET (or any other method) we'll create a blank form
else:
form = MyForm()
return render(request, 'my_other_template.html', {'form': form})
Display result in your template:
<!-- my_template.html -->
<div>{{ result }}</div>
Maybe you need to be more specific with your question, this is what I think you are looking for though. You should put logic like you are describing into a view, not a template.
Related
Hello I am making django to-do list and I have a question. Am i able to delete and create elements in one view? Personally I would not use CreateView for that and make it like in 'def post' and ignore 'get_success_url' but is it good and djangonic practice?
views.py
class Home(CreateView):
model = Item
template_name = 'home/todo.html'
form_class = itemCreationForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['items'] = Item.objects.all()
return context
def get_success_url(self):
return reverse('home-page')
def post(self, request):
form = itemCreationForm()
if 'delete' in self.request.POST:
print('deleted')
return redirect('home-page')
if 'add' in self.request.POST and form.is_valid():
form.save()
return HttpResponse('home-page')
HTML
<div class="card__items-list">
{% for item in items %}
<div class="card__item-row">
<p class="card__item">{{ item.name }}</p>
<form action="{% url 'home-page' %}" class="card__delete" method="POST"> {% csrf_token %}
<button class="card__delete__button" name="delete" type="submit">✘</button>
</form>
</div>
{% endfor %}
</div>
<form name="add" method="POST"> {% csrf_token %}
{{ form }}
<button name="add" class="submit-button" type="submit">Submit</button>
</form>
</div>
It is possible to handle both the deletion and creation of objects in a single Django view function. However, it is arguably a better practice to handle these operations in separate HTTP action verbs (POST vs DELETE).
For example, your Home view could handle both of these operations, but separate them into different if blocks based on the HTTP action verb of the request. Then, from your web page, the create operation would send an HTTP POST request, and the delete operation would send an HTTP DELETE request.
This could be done in a single Django view function using the following sample code:
def my_sample_view(request):
if request.method == "POST":
# Handle create logic here.
elif request.method == "DELETE":
# Handle delete logic here.
Yes, you can do this.
Personally, I would use FormView not CreateView, and do the POST processing in the form_valid method. It's contrary to one's expectations for a CreateView to delete something, whereas processing a form can basically do anything.
I am writing a very basic web page in Python which has a text box where a user can type in a username, then hit the Ok button which submits a form using a GET request. The GET passes the username as an argument and searches the auth_user table in the database.
My problem is I am not able to pass the username argument, please help if you can Django 2.0 url patterns
urls.py
app_name = 'just_gains'
urlpatterns = [
path('lifecoaching', views.LifeCoach, name='life_coaching'),
path('lifecoaching/resultslifecoaching/<str:user_name>', views.LifeCoachSearchResults, name='results_life_coaching'),
]
forms.py
class LifeCoachSearch(forms.Form):
user_name = forms.CharField(label='Username', max_length=100, required = False)
views.py
def LifeCoach(request):
if request == 'GET':
form = LifeCoachSearch(request.GET)
if form.is_valid:
user_name = form.cleaned_data['user_name']
LifeCoachSearchResults(request,user_name)
else:
form = LifeCoachSearch()
return render(request, 'just_gains/life_coaching.html', {'form': form})
def LifeCoachSearchResults(request, user_name):
testUser = User.objects.filter(username__startswith=user_name)
context = {'TestUser': testUser}
return render(request, 'just_gains/results_life_coaching.html', context)
HTML (lifecoaching)
<form action="{% url 'just_gains:results_life_coaching' %}" method="GET" >
{% csrf_token %}
{{ form }}
<input type="submit" value="OK">
</form>
HTML (resultslifecoaching)
<ul>
<li><a>print usernames that match the argument</a></li>
</ul>
Forgive me for the short response as I am on mobile. Try passing your username as a string in the path using <str:user_name>
Usually I think the form should submit via POST rather than GET, and the value of the submitted username would then be available in the dictionary request.POST['username']. GET should be used to get forms from the server; POST posts information back to the server. POST ensures that the browser bundles everything in the form and sends it complete, but GET tries to encode it in the URL and makes no guarantees.
Using forms, its helpful to have the View divide so that GET requests pull up blank or prepopulated forms (the empty search box) and POST requests are processed and redirected to the parameterized results screen you have.
You would then create a httpRedirect to re-assign the request to your URL with a parameter. I think this link, example 2 is the right way to go.
https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#redirect
So your function would look like:
def LifeCoach(request):
if request.method = 'GET':
return render(request, 'just_gains/life_coaching.html', context)
elif request.method = 'POST':
# I have skipped form validation here for brevity
return redirect('results_life_coaching',request.POST['username'])
It's possible that having a field called username may clash with or confuse you later when using request.USER['username']. Don't forget to change your form html! All the best!
[Edit 1] My code was wrong; GET should call the lifecoaching form, POST should redirect to the results_life_coaching page.
[Edit 2] My suggestions for your templates:
HTML (lifecoaching.html)
<form action="{% url 'just_gains:life_coaching' %}" method="POST" >
{% csrf_token %}
{{ form }}
<input type="submit" value="OK">
</form>
HTML (resultslifecoaching.html)
<ul>
{% for item in username_list %}
<li>{{item.user_name}} - {{item.achievement}} </li>
{% endfor %}
</ul>
Django says, i should render inline formset this way:
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
{{ form.field_1 }}
{{ form.field_2 }}
<button type="button"> delete </button>
{% endfor %}
<button type="submit"> submit </button>
Ok. But what if i want to delete some formset objects (form) dynamically? User press delete button - i remove form from the DOM, i use ajax to remove object, related to the form from the DATABASE. It works ok till this point. But when user clicks submit - my views.py trying to validate formset:
filled_formset = OrderItemFormSet(request.POST, instance=order)
if filled_formset.is_valid():
and raises error:
MultiValueDictKeyError at /order/cart/
"'orderitem_set-0-id'"
...\market\ordersys\views.py in show_cart
59. if filled_formset.is_valid():
I think it happend because form objects were displayed by django with some regularity (first form got id = orderitem_set-0-id, second = orderitem_set-1-id etc.) And when i deleted first form from the DOM, the regularity was broken - there is no more form with orderitem_set-0-id. But is_valid() still trying to get it dict["orderitem_set-0-id"].
I could use some black magic, substituting django's technical information, displayed in the DOM, restoring disrupted regularity, but is there a better way?
Could you tell me how to properly dynamically delete formset items, please ?
I got no answer for some time, so i did not find any better solution than below. Maybe someome will find it useful.
Ok, the trick is - to have {{form.DELETE}} for any form in your formset in template. It renders as a checkbox (i made it invisible) and i made JS to make it "checked" whenever user press "delete" button. After user press "submit" button, every form, marked for deletion, will NOT be validated by the view during filled_formset.is_valid(). This lets you delete object from database with ajax behind the scene.
The problem was that an ERROR was raised during formset validation. Caused by the form of an object, which was already deleted from database with ajax.
So there are all components:
views.py
def show_cart(request):
OrderItemFormSet = inlineformset_factory(Order, OrderItem, form=OrderItemForm, extra=0, can_delete=True)
order = Order.objects.get(pk=request.session['order'])
if request.method == 'GET':
formset = OrderItemFormSet(instance=order)
return render(request, 'ordersys/cart.html', {'formset': formset})
elif request.method == 'POST':
filled_formset = OrderItemFormSet(request.POST, instance=order)
if filled_formset.is_valid():
filled_formset.save()
return redirect('catalog:index')
else:
return render(request, 'ordersys/cart.html', {'formset': filled_formset})
cart.html
<form action="" method="post">
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
{{ form.DELETE|add_class:"not_displayed" }} # custom filter
<img src="{{ form.instance.spec_prod.product.picture.url }}">
{{ form.quantity.label_tag }}
{{ form.quantity }}
{{ form.errors }}
<button type="button">Delete</button>
{% endfor %}
<button type="submit">Submit</button>
</form>
Next, if user press 'DELETE' button, my JavaScript
1. hides form with $(item).css('display', 'none');
2. makes checked form.DELETE checkbox with ItemDelCheckbox.prop('checked', true);
3. sends ajax request to delete item from database (otherwise if user refreshes the page, the item still in the cart)
views.py
def delete_order_item(request): # meets the ajax request
item_id = int(request.POST['item_id'])
order = get_object_or_404(Order, pk=int(request.POST['order_id']))
order.remove_item(item_id)
if order.is_empty(): # if the last item is deleted
order.untie(request.session)
order.delete()
return HttpResponse()
Instead of manually creating the hidden field using {{ form.DELETE }}, you can probably use 'can_delete' while instantiating the formset which does the same. For eg,
ArticleFormSet = formset_factory(ArticleForm, can_delete=True)
Refer can_delete
i have created a delete function for my forms but it can only delete if i did not display out my information.
However if i try to display the information out on the delete page, the delete function wouldn't work.
i think because when i display the information out, the form isn't valid, hence unable to delete the object.
My views.py
def servicing_entry_delete(request, serv_entry_id):
delete_object = get_object_or_404(serv_entry, id=serv_entry_id)
if request.method == 'POST':
form = Delete_Flying_form(request.POST, instance=delete_object)
if form.is_valid(): # checks CSRF
delete_object.delete()
return HttpResponseRedirect(reversed('hello:servicing_entry')) # wherever to go after deleting
else:
form = Delete_Flying_form(instance=delete_object)
return render(request, 'hello/servicing_entry_delete.html', {'form':form})
My forms.py
class Delete_Flying_form(forms.ModelForm):
class Meta:
model = serv_entry
fields = ('Servicing_Type', 'Time', 'Date','Ic_Clear_Name','Ic_Clear_Time','Ic_Clear_Date')
My deleteconfirmation.html
{% extends 'hello/base.html' %}
{% block content %}
<h1>Delete Servicing Entry</h1>
<p>Are you sure you want to delete this Entry: </p>
{{ form }}
<form action="" method="POST">
{% csrf_token %}
<input type="submit" action="" value="Yes, delete this Entry." />
</form>
{% endblock %}
Without the {{ form }}, i am able to delete the object smoothly but not being able to display any information.
However if i try to display, like this
is there anyway if i could display this few fields and still delete successfully?
My django version is 1.11
with python 2.7.5
Much appreciate!
I have a form with several input fields about a meeting.
#app.route('/Alt_Reuniao/<WCodigoChaveP>', methods=['GET', 'POST'])
def Alt_Reuniao(WCodigoChaveP):
# This function returns a list with de data from the Database about a specific meeting
WReuniao = Executa_Query_Leitura("0005", WCodigoChaveP, "O")
if not WReuniao:
flash('Error.......')
return redirect(url_for('Cad_Reunioes'))
else:
if WReuniao[8]:
# this function returns a list with de data from the Database about the topic of the meeting
WAssunto = Executa_Query_Leitura("0002", WReuniao[8], "O")
Wform = Cad_Reunioes_Form()
if request.method == "POST":
if Wform.validate():
# save the data ......
return redirect(url_for('Cad_Reunioes'))
else:
for Werro in Wform.errors.values():
flash(Werro[0])
return render_template('Reuniao.html', Wformulario=Wform)
else:
Wform.WCPO_Reuniao.data = WReuniao[1]
Wform.WCPO_Desc_Reuniao.data = WReuniao[2]
Wform.WCPO_Nro_Part.data = WReuniao[3]
Wform.WCPO_Cod_Assunto.data = WReuniao[8]
if WReuniao[8]:
if WAssunto:
Wform.WCPO_Assunto.data = WAssunto[1]
return render_template('Reuniao.html', Wformulario=Wform)
This is my Reuniao.html template:
{% extends "Base_Cad_2.html" %}
{% block dados %}
{{ Wformulario.WCPO_Reuniao.label(id="WCPO_Reuniao", class="lab1") }} {{ Wformulario.WCPO_Reuniao(size = 100, maxlength=30, id="WCPO_Reuniao") }}
<p id="PL"> {{ Wformulario.WCPO_L_Desc_Reuniao(id="WCPO_L_Desc_Reuniao", class="lab1") }} </p>
{{ Wformulario.WCPO_Desc_Reuniao(rows=5, cols=100, id="WCPO_Desc_Reuniao") }}
{{ Wformulario.WCPO_Nro_Part.label(id="WCPO_Nro_Part", class="lab1") }} {{ Wformulario.WCPO_Nro_Part(size = 5, id="WCPO_Nro_Part") }}
{{ Wformulario.WCPO_Cod_Assunto.label(id="WCPO_Cod_Assunto", class="lab1") }} {{ Wformulario.WCPO_Cod_Assunto(size=10, readonly='readonly', id="WCPO_Cod_Assunto") }}
{{ Wformulario.WCPO_Assunto.label(id="WCPO_Assunto", class="lab1") }} {{ Wformulario.WCPO_Assunto(size=95, readonly='readonly', id="WCPO_Assunto") }}
<button id="Selec_Assunto" name="Selec_Assunto" value="Selec_Assunto" type="button"> <a class="botoes" href="{{ url_for('Selec_Assuntos_Inicio', WRotChama = "001", WCodorig = Wformulario.WCPO_Cod_Reuniao ) }}" hreflang="pt-br"> Seleciona </a> </button>
{% endblock %}
{% block botoes %}
<button id="gravar" name="gravar" value="Gravar" type="submit" class="botoes" > Gravar </button>
{% endblock %}
Basically, this view works fine.
When I select a meeting from a list in a previous template the view method is a GET and the data from the database is passed to the form and the template renders correctly.
When the method is a POST the data from the form is saved correctly in the Database,…
On the form there is a button Selec_Assunto. When the user click on that button I point to a view witch renders a template with a list of all possible topics for the meeting. These topics come from the database. There can be a lot of them so I can’t just use a combo. That’s why I use a template.
When the user select an topic from the list I have to render Alt_Reuniao view again and I have to pass to the view the selected topic.
This is working fine.
My problem is this: the method again is a GET. If before hitting the Selec_Assunto button the user alter or input data on the other fields in the form I loose these data from the user when the topic is selected. The view render the template with the data from the database again.
Everything seems to be working fine. I just want to maintain the data the user already change in the form before clicking on the Selec_Assunto button.
As you can see I’m new in Web developing, Python, Flask,…
Thanks a lot in advance for your help.
In this case you could update the "Selec_Assunto" button behavior to post the form data back to the server, but also include a redirect variable. When the redirect variable is included, the server would save the form changes and then redirect to the "Selec_Assuntos_Inicio" view, whereas it would follow the previous/normal form posting behavior if the redirect variable isn't present. For example:
if request.method == "POST":
if Wform.validate():
# save the data ……
if Wform.redirect.data:
return redirect(Wform.redirect.data)
else:
return redirect(url_for('Cad_Reunioes'))
else:
for Werro in Wform.errors.values():
flash(Werro[0])
return render_template('Reuniao.html', Wformulario=Wform)
It's worth noting that this approach requires you to use JavaScript to override the "Selec_Assunto" button behavior (as you'd be forcing it to perform a form submission essentially). Here's one way you could do that using jQuery:
$('button#Selec_Assunto').on('click', function() {
$('form#formId').append('<input type="hidden" name="redirect" value="{{ url_for('Selec_Assuntos_Inicio', WRotChama = "001", WCodorig = Wformulario.WCPO_Cod_Reuniao ) }}">');
$('form#formId').submit();
});
That said, a potentially better option from both a coding and usability perspective would be to asynchronously load the topic data into the existing page so that you don't have to reload the view at all. That would enable you to avoid needing to do an interim save of the form data.