I have an update view which displays a form (based on some criteria). I can update my model successfully, but if I enter bad data (like a bad time field), I do not get any errors back, I simply get a refresh of the update form.
My view looks like this:
class Cd_MixedView(UpdateView):
model = Track
template_name = 'cd_mixed_view.html'
form_class = TrackForm
context_object_name = 'cd_edit'
def get_context_data(self, **kwargs):
context = super(Cd_MixedView,self).get_context_data(**kwargs)
cur_track = Track.objects.get(id=self.kwargs['pk'])
context['form'] = TrackForm(instance=cur_track)
context['cd_info'] = Cd.objects.get(id=cur_track.cd_id.pk)
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self,form):
form.save()
return super(Cd_MixedView, self).form_valid(form)
def form_invalid(self,form):
print("Form Invalid")
return super(Cd_MixedView,self).form_invalid(form)
My template is the following:
<!-- templates/cd_mixed_view.html -->
{% extends 'base.html' %}
{% load static %}
{% block title %} CD Details{% endblock title %}
{% block content %}
<h1>CD Update Track </h1>
{% if cd_info %}
<p>Artist Name: {{ cd_info.artist_name}}
<p>Cd Title: {{ cd_info.cd_title }}
<p>Cd Total Time: {{ cd_info.cd_total_time|time:"H:i:s" }}
<p>Cd Run Time: {{ cd_info.cd_run_time|time:"H:i:s" }}
<p>Cd Remaining Time:
{% if cd_info.cd_run_time_delta > cd_info.cd_total_time_delta %}
(-{{ cd_info.cd_remaining_time|time:"H:i:s" }})
{% else %}
{{ cd_info.cd_remaining_time|time:"H:i:s" }}
{% endif %}
<TABLE BORDER="0" TABLE_LAYOUT="fixed" WIDTH="100%">
<TR BGCOLOR="#B0B0FF">
<TD ALIGN="Center"> Track #</TD>
<TD ALIGN="Center"> Cut Title</TD>
<TD ALIGN="Center">Track Length</TD>
<TD ALIGN="Center" BGCOLOR="#CC99CC">Run Time</TD>
<TD ALIGN="Center" BGCOLOR="#CC99CC">Time Remaining</TD>
</TR>
{% for tracks in cd_info.cd_tracks.all %}
{% if tracks.id != cd_edit.pk %}
<TR>
<TD ALIGN="Left" VALIGN="Top" WIDTH="10"> {{ tracks.track_number }}</TD>
<TD ALIGN="Left" VALIGN="Top"> {{ tracks.track_title }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ tracks.trk_length_time|time:"H:i:s" }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ tracks.trk_run_time|time:"H:i:s" }}</TD>
{% if tracks.trk_run_time_delta > cd_info.cd_total_time_delta %}
<TD BGCOLOR="#8DF1BF" ALIGN="Left"> (-{{ tracks.trk_remaining_time|time:"H:i:s" }})</TD>
{% else %}
<TD BGCOLOR="#8DF1BF" ALIGN="Left"> {{ tracks.trk_remaining_time|time:"H:i:s" }}</TD>
{% endif %}
</TR>
{% else %}
<form action="" method="post">
{% csrf_token %}
{{ form }}
<tr>
<td> </td>
<td><input type="submit" value="Update Track Values"></td>
</tr>
</form>
{% if form.errors %}
<p>There were some errors in the information you entered. Please correct the following:</p>
<ul>
{% for field in form %}
{% if field.errors %}
<li>{{ field.label }}:
{{ field.errors|striptags }}</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
{% endif %}
{% endfor %}
</TABLE>
{% endif %}
{% endblock content %}
The form itself is the following:
class TrackForm(forms.ModelForm):
class Meta:
model = Track
exclude = ('trk_length_time_delta',
'trk_run_time',
'trk_run_time_delta' ,
'trk_remaining_time',
'trk_remaining_time_delta',
)
widgets = {
'cd_id': forms.HiddenInput()
}
I even put a print in the invalid_form function - so it seems to know the form is invalid,
but it just redisplays my form with the previous data.
Any ideas what I am missing here?
Thanks
You use UpdateView yet for some reason you go and redefine most of it's useful methods while not even making any useful or new changes. The main problem is that you override get_context_data and overwrite the form while manually defining it even though the super method has already added the form properly to the context.
Let UpdateView do it's job most of the code you write is not needed as UpdateView already does that:
class Cd_MixedView(UpdateView):
model = Track
template_name = 'cd_mixed_view.html'
form_class = TrackForm
context_object_name = 'cd_edit'
def get_context_data(self, **kwargs):
context = super(Cd_MixedView,self).get_context_data(**kwargs)
cur_track = self.object # no need to get the object again it is already present in `self.object`
# form is already added to context by super method
context['cd_info'] = Cd.objects.get(id=cur_track.cd_id.pk)
return context
# `post`, `form_valid` and `form_invalid` methods were same as super so no need to write yourself
Related
I have a Django formset that is displayed as a table with one form per table. I would like to add a checkbox in the first column of the table so that the user to check it if they would like to delete the row (form).
I have the javascript to manage the deletion of the formset row (form) and modify the management form on the front end, but I am having an issue when I add the DELETE field to the form. I used the solution reference in modify DELETE widget so that I could add the class "delete" to all of my delete fields for use in CSS and JS on the front end. When the DELETE field is added it is always the last field in the form. I would like it to be first.
models.py
class ModelOne(models.Model):
attr_one = models.CharField(max_length=16)
attr_two = models.CharField(max_length=16)
forms.py
class BaseFormOneFormset(BaseModelFormSet):
def add_fields(self, form, index) -> None:
super().add_fields(form, index)
form.fields[DELETION_FIELD_NAME].widget = forms.CheckboxInput(attrs={'class':"delete"})
form.fields["id"].widget=forms.HiddenInput(attrs={'class':'pk'})
class FormOne(forms.ModelForm):
class Meta:
model = ModelOne
attr_one = forms.CharField(max_length=16,
required=True,
label="attr_one",
widget=forms.TextInput(attrs={'size':5,'class':'required '}))
attr_two = forms.CharField(max_length=16,
required=True,
label="attr_two",
widget=forms.TextInput(attrs={'size':5,'class':'required '}))
views.py
def view_one(request):
formset_factory_one = modelformset_factory( ModelOne,
FormOne,
formset=BaseFormOneFormset,
extra=0,
can_delete=True)
formset_one = formset_factory_one(query_set=FormOne.objects.all(),
prefix="formone")
return render(request, "app_one/template_one.html",{"formset_one":formset_one})
template_one.html
<table id="tbl-id">
<thead id="tbl-head-id">
<tr>
{% for form in formset_one|slice:":1" %}
{% for field in form.visible_fields %}
<th>{{field.label|safe}}</th>
{% endfor %}
{% endfor %}
</tr>
</thead>
<tbody id="tbl-body-id">
{% for form in formset_one %}
<tr id="row{{forloop.counter0}}-id" class="formset-row">
{% for field in form.visible_fields %}
<td>
{{field}}
</td>
{% endfor %}
{% for field in form.hidden_fields %}
<td hidden >{{field}}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
Resulting Table
image of resulting html table
Solutions tried
I have already tried to set the Form.field_order both in the FormOne class declaration
class FormOne(forms.ModelForm):
field_order = [ DELETION_FIELD_NAME, "attr_one", "attr_two"]
and in the BaseFormset.add_fields method
def add_fields(self, form, index) -> None:
super().add_fields(form, index)
form.fields[DELETION_FIELD_NAME].widget = forms.CheckboxInput(attrs={'class':"delete"})
form.fields["id"].widget=forms.HiddenInput(attrs={'class':'pk'})
form.field_order = [ DELETION_FIELD_NAME, "attr_one", "attr_two"]
These both result in the DELETE field still last in the order.
Although there are ways to change the order of the DELETE field by overriding the formset's add_fields() function, it is pretty tedious and kinda complicated.
However, if your goal is to simply render DELETE first, or in a completely different part of the page, you are in luck. This is easily accomplished in the template.
The delete field can be rendered explicitly in the template using {{ form.DELETE }} which is equivalent to the regular output of {{ field }} that you get when iterating through form.fields.
Simple example to render DELETE field first:
{# this renders the DELETE field #}
{{ form.DELETE }}
{# now render the other fields #}
{% for field in form.visible_fields %}
{# if check to prevent DELETE rendering twice #}
{% if field.name != 'DELETE' %}
{{ field }}
{% endif %}
{% endfor %}
Applied to the code from your question:
<table id="tbl-id">
<thead id="tbl-head-id">
<tr>
{% for form in formset_one|slice:":1" %}
{% for field in form.visible_fields %}
<th>Delete</th>
{% if field.name !='DELETE' %}
<th>{{field.label|safe}}</th>
{% endif %}
{% endfor %}
{% endfor %}
</tr>
</thead>
<tbody id="tbl-body-id">
{% for form in formset_one %}
<tr id="row{{forloop.counter0}}-id" class="formset-row">
<td>
{{ form.DELETE }}
</td>
{% for field in form.visible_fields %}
{% if field.name != 'DELETE' %}
<td>
{{field}}
</td>
{% endif %}
{% endfor %}
{% for field in form.hidden_fields %}
<td hidden >{{field}}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
I am working on a Supplier Management System. I have a page where users could view all suppliers on the application. on the same page, users can upvote and downvote suppliers, is there a way that i could make the supplier not able to upvote or downvote his self.
I am thinking if there is a way i can pass the supplier.user into the context which identifies the supplier, perhaps doing something like this but it doesn't seem to work. I could then pass this into the template {% if supplier.user == request.user %}content...{% endif %}
Presently, supplier.user return nothing.
views.py
def Viewsupplier(request):
title = "All Suppliers"
suppliers = User.objects.filter(user_type__is_supplier=True)
# Get the updated count:
suppliers_votes_count = {}
for supplier in suppliers:
upvote_count = supplier.upvotes
downvote_count = supplier.downvotes
supplier_count = {supplier: {'upvote': upvote_count, 'downvote': downvote_count } }
suppliers_votes_count.update(supplier_count)
context = {"suppliers":suppliers, "title":title, "suppliers_votes_count": suppliers_votes_count}
return render(request, 'core/view-suppliers.html', context)
view-supplier.html
<thead>
<tr>
<th>No</th>
<th>Email</th>
<th>Votes</th>
</tr>
</thead>
<tbody>
{% for supplier in suppliers %}
<tr>
<td>{{forloop.counter}}</td>
<td>{{supplier.email}}</td>
<td>
<div class="table-data-feature">
{% if supplier.user == request.user %}<a href="{% url 'upvote' supplier.id %}">{% endif %}
<button class="item" data-toggle="tooltip" data-placement="top" title="Like">
<i class="zmdi zmdi-thumb-up"></i></button>
{% if supplier.user == request.user %}</a>{% endif %}
<button>{{supplier.upvotes}}</button>
{% if supplier.user == request.user %}<a href="{% url 'downvote' supplier.id %}">{% endif %}
<button class="item" data-toggle="tooltip" data-placement="top" title="Dislike">
<i class="zmdi zmdi-thumb-down"></i></button>
{% if supplier.user == request.user %}</a>{% endif %}
<button>{{supplier.downvotes}}</button>
</div>
</td>
</tr>
{% empty %}
<tr><td class="text-center p-5" colspan="7"><h4>No supplier available</h4></td></tr>
{% endfor %}
</tbody>
I was able to solve the issue by using this templatetag in my templates:
{% if supplier.email != request.user.email %}content...{% endif %}
This worked easily because i already had the supplier info from the views already.
My django version is 1.11.4, and I'm on a simple exercise in my book which I need to make a web page that people can submit their comments for a selected restaurant.
But it shows the message Forbidden (CSRF token missing or incorrect.).
views.py :
def comments(request, id):
if id != 0:
r = Restaurant.objects.get(id = id)
else:
return HttpResponseRedirect('/restaurantsList/')
if request.POST:
dateTime = timezone.localtime(timezone.now())
Comment.objects.create(
content = request.POST['content'],
visitor = request.POST['visitor'],
email = request.POST['email'],
dateTime = dateTime,
restaurant = r
)
return render_to_response('comments.html', locals(), RequestContext(request))
comments.html :
<!doctype html>
<html>
<head>
<title>Comments</title>
<meta charset='utf-8'>
</head>
<body>
<h2>Comments for {{ r.name }}</h2>
{% if r.comment_set.all %}
<p>We have {{ r.comment_set.all | length }} comments</p>
<table>
<tr>
<th>Visitor</th>
<th>Time</th>
<th>Comment</th>
</tr>
{% for c in r.comment_set.all %}
<tr>
<td>{{ c.visitor }}</td>
<td>{{ c.dateTime | date:'F j, Y' }}</td>
<td>{{ c.content }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<p>No comment</p>
{% endif %}
<br /><br />
<form action='' method='post'> {% csrf_token %}
<table>
<tr>
<td><label for='visitor'>Visitor: </label></td>
<td><input id='visitor' type='text' name='visitor'></td>
</tr>
<tr>
<td><label for='email'>E-mail: </label></td>
<td><input id='email' type='text' name='email'></td>
</tr>
<tr>
<td><label for='content'>Comment: </label></td>
<td>
<textarea id='content' rows='10' cols='48'
name='content'></textarea></td>
</td>
</tr>
</table>
<input type='submit' value='Submit'>
</form>
</body>
I've added {% csrf_token %} in .html, used RequestContext(request) in view function and tried several ways I searched from the Internet. But it still doesn't work.
Can somebody give me a help? Thanks!
You should use a book that is more up to date. render_to_response is deprecated and RequestContext should not be used here. Also, passing locals is a horrible anti-pattern, although not the cause of your problem.
return render(request, 'comments.html', {'r': r})
I'm a django newbie, I'm making a little app to record work calls received during nighttime.
At the moment the app works fine. I have made a form to fill call data, which is then presented as a table using a generic ListView.
class IndexView(LoginRequiredMixin, generic.ListView):
login_url = '/login/'
redirect_field_name = 'redirect_to'
template_name = 'chiamate/list_full.html'
context_name = 'lista_chiamate'
def get_queryset(self):
return Chiamata.objects.all
Here is the code for the template:
{% extends 'chiamate/base.html' %}
{% block content %}
{% if user.is_authenticated %}
<i class="fa fa-plus-square-o fa-lg" aria-hidden="true"></i> Nuova Chiamata
<p></p>
{% else %}
<p>Welcome, Guest please [login]</p>
{% endif %}
<div class="table-responsive">
<table class="table table-striped">
<form action="{{ action }}" method="post">
{% csrf_token %}
{% for chiamata in object_list %}
{% if chiamata.data_chiusura.weekday == 4 or chiamata.data_chiusura.weekday == 5 or chiamata.data_chiusura.weekday == 6 %}
<tr class="info">
{% elif chiamata.data_chiusura == None %}
<tr class="danger">
{% else %}
<tr>
{% endif %}
<td><input class="checkbox" name="chiamata_mail" type="checkbox" id="checkbox_{{ chiamata.id }}"value="{{ chiamata.id }}"></td>
<td class="txtdata">{{ chiamata.utente.get_full_name|wordwrap:5 }}</td>
<td class="txtdata">{{ chiamata.data_chiamata|date:"l d M Y H:i" }}</td>
<td>{{ chiamata.interlocutore }}</td>
<td>{{ chiamata.testo_chiamata|truncatechars:200 }}</td>
<td class="txtdata">{{ chiamata.reperibile|wordwrap:5 }}</td>
<td>{{ chiamata.data_chiusura|date:"H:i" }}</td>
<td></i> Edit</td>
<td><i class="fa fa-trash-o fa-lg" aria-hidden="true"></i> Delete</td>
</tr>
{% endfor %}
<input id="send_selected" type="submit" value="Send" />
</form>
</table>
</dv>
{% endblock %}
I've already tried to add the checkboxes here. When I do, they show on the page but then when I press the send button (currently implemented as a form send, but I want to use a bootstrap button) I don't know how to pass the objects to the email function, or at least show them in a new page.
You should define the post method of your view:
class IndexView(LoginRequiredMixin, generic.ListView):
def post(self, request, *args, **kwargs):
return ChiamataUpdateView.as_view()(request)
And handle the post data on the other view.
Also, you get_queryset isn't returning a queryset since you're not calling all(). It should be:
return Chiamata.objects.all()
I am trying to get the first or nth iteration only in a Django template.
Usually I can iterate through using,
{% for item in pModel %}
{{ item.post }}
{% endfor %}
I need the first iteration but would also like to know how to get the n-th iteration,
{{ pModel.0.post }}` displays nothing and gives no error.
I don't want to iterate through every object in pModel.
I have tried all combinations i.e.
{{ pModel[0][post] }}
{{ pModel.0.[post] }}
{{ pModel[0].post }}
{{ pModel[0][post] }}
{{ pModel.[0][post] }}
{{ pModel.[0].[post] }} etc.
The pModel comes from this view,
def profile(request, id):
pk = id
name = User.objects.all().filter(id=pk)
pModel = reversed(PostModel.objects.all().filter(author = name[0]))
# user_instance = User.objects.all().filter(username = request.user)
return render(request, 'profile.html', {'pModel': pModel, 'current_time': timezone.now()})
The following display nothing,
<strong>{{ pModel.first.post }}</strong>
In the same template I use the pModel which displays correctly so i know that the pModel is working. The complete template,
{% extends 'index.html' %} {% block homepage %}
<div class="post">
{% if pModel %}
<h3>Profile for <strong>{{ pModel.first.post }}</strong></h3>
<p>Last logged in: {{user.last_login|timesince:current_time}} ago on {{ user.last_login }}</p>
<p>Joined {{user.date_joined|timesince:current_time}} ago on {{ user.date_joined }}</p>
{% endif %}
{% if pModel %}
<div class="table-responsive">
<table class='table table-striped table-hover'>
<thead>
<tr>
<th>{{user.username}}'s posts</th>
<th>Topic</th>
<th>Topic Started By</th>
<th>Last Active</th>
<th class="table-cell-center">Views</th>
</tr>
</thead>
<tbody>
{% for item in pModel %}
<tr>
<td>{{ item.post }} uuu {{ pModel.0}}</td>
<td>{{ item.topic.topic }}</td>
<!-- item.topicid.authorid_id -->
<td>{{ item.topic.topicAuthor }}</td>
<td class="icon-nowrap">{{ item.pub_date|timesince:current_time}}</td>
<td class="table-cell-center">{{ item.topic.views }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{% endblock %}
Your pModel variable is not a queryset or a list, but a reverse iterator. You cannot access individual elements of an iterator, you can only iterate over the iterator once, exhausting it in the process.
To support access of individual elements, you need to convert pModel to a sequence, such as a list:
pModel = list(reversed(PostModel.objects.filter(author = name[0])))
You can then access the index in your template:
{{ pModel.0.post }}
You can use the forloop.counter0 template variable. For example, to access the n'th element:
{% for item in pModel %}
{% if forloop.counter0 == n %}
{{ item.post }}
{% endif %}
{% endfor %}
You can also use first as a special case:
{{ item.first.post }}