all_I'm having a hard time having an attribute that I'm adding to a QS to show up in my template.
I have a manager that looks something like this:
def created_by(self, _userProfile, dateGt=None, dateLt=None):
results = self.filter(creator=_userProfile, ...) //Some QS here
for result in results:
result.finished = "It works!"
for result in results:
print result.finished
return results
This prints in my console: (I have 2 events). I did the double for loop as a debug thing to make sure that after exiting the for loop the data was still there.
It works!
It works!
And then I'm calling the manager:
events = Event.manager.created_by(some stuff here).order_by('-id')
And then the weird part: I'm passing the results of the line above to the template and show something like that:
{% for event in all_events %}
<li>{{ event.title }} Finished:{{ event.finished }}</li>
{% empty %}
<em>{% trans "NOTHING" %}</em>
{% endfor %}
And everything shows up (title, id, etc) but not the "finished" part.
Any idea where that could come from ? I trimmed the code for readability, but what I don't think that anything I removed is relevant here.
Thanks!
EDIT:
Here's the view (weird indentation because of stackoverflow 4 spaces thing)
def homepage(request):
variables = {}
if request.user.is_authenticated():
datenow=datetime.now()
me = request.user.get_profile()
variables['all_events'] = Event.manager.created_by(me).order_by('-id') [:20]
context_instance = RequestContext(request)
template_name = "common/homepage.html"
return render_to_response(template_name, variables, context_instance)
I ended up creating a finished() method in the Event model and call directly event.finished in the template.
It works like that, but I still don't know why my first setup did not
Related
I have a view function like this:
#login_required
def delete_deal(request, id_deal):
id_deal = int(id_deal)
user = get_object_or_404(User, id=request.user.id)
user_deals = Deal.objects.filter(user=user)
if not user_deals.exists():
messages.add_message(request, messages.INFO,
'You have no deal.')
return redirect('index')
return render(request, 'booking/confirmation_delete.html', {'title': title})
If I test with a user who does not have a deal the message is not displayed the first time but if I redo the message is displayed 2 times, what is wrong ?
I have another view function which returns a message and it displays it normally !!
If you need more info tell me
Update:
Here is my url.py of the application where the view function above is located:
urlpatterns = [
url(r'^create_deal/$', views.create_deal, name='create_deal'),
url(r'^delete_deal/(?P<id_deal>[0-9]+)/$', views.delete_deal, name='delete_deal'),
]
The view.py function:
def index(request):
all_deals = Deal.objects.all()
return render(request, 'base.html', locals())
template.html:
{% if messages %}
{% for message in messages %}
<div>{{ message }}</div>
{% endfor %}
{% endif %}
It looks like it adds the message both times and will redirect to 'index' both times... So perhaps the problem is either in the urls and view for 'index' or somehow in the template being rendered. Show us the urls.py, the associated view function/opject and template.
I had this issue because to display the message I tried to enter in the search bar an url towards a deal which does not exist, and looking in the console I saw that passing the url in the search bar the view is executed before even hitting enter
My url was something like this:
http://127.0.0.1:8000/booking/delete_deal/65/
and the number 65 is an id_deal which does not exist
I tried to add a link that redirect to the view function with an id_deal which does not exist and the message is always shown
Take this example form.
class TestForm(forms.Form):
def __init__(self, *args, **kwargs):
super(TestForm, self).__init__(*args, **kwargs)
self.fields["test"] = forms.CharField()
In my view, I have this code:
test_form = TestForm()
context['test_form'] = test_form
If I print(test_form), it will appear just fine. Now, when I use {% debug %} in my template, this is the output: { ... 'test_form': , ... }. In other words, the form disappears. I suspect it has something to do with adding a field after initialization, but I would really like to know why? Also how to get around the seeming limitation, if possible.
To test if your form has disappeared, you can try displaying it:
{% for field in test_form %}
{{ field }}
{% endfor %}
This should display the each field of the form as HTML. Hope it helps!
Your contact variable hasn't disappeared: it's right there, in the output you show us. If it had disappeared, you wouldn't see the test_form key in that dictionary.
As to why you don't see a value for that key, that's simply because the default representation of a python object is like this <TestForm object at ....>, and your browser is interpreting the angle brackets as an unknown HTML tag which it ignores.
Put your debug tag within <pre>...</pre> tags and you'll see it just fine.
I am changing a list of field's values upon viewing, but I want to pass the unchanged values to the context. The case here is of a primitive notification system where upon viewing the notification, it should change its status to viewed.
views.py
class Notification(TemplateView):
template_name='myapp/notification.html'
def get_context_data(self, **kwargs):
user = self.request.user
user_unread = user.notification_set.filter(viewed=False)
user_read = user.notification_set.filter(viewed=True)
context = super(Notification, self).get_context_data(**kwargs)
context.update({'user_unread': user_unread, 'user_read': user_read})
for msg in user_unread:
msg.viewed = True
msg.save()
return context
The Problem with this code however, is that I am getting duplicated values in the read and unread lists, even though I have saved the new values to the model after updating the context that is passed to the template.
template:
Unread:
<ul>
{% for msg in user_unread %}
<li> {{ msg }} </li>
{% endfor %}
</ul>
Already read:
<ul>
{% for msg in user_read %}
<li> {{ msg }} </li>
{% endfor %}
</ul>
On a sidenote, I am new to CBVs and if there if my view code above could be improved I'd love some pointers.
This is due to the lazy nature of querysets. A queryset does not actually touch the database until you evaluate it, which usually occurs when you iterate. So in your code, you iterate through user_unread in the view, to set the read status: so the contents of the queryset are fixed at that point. But you don't iterate through user_read until you reach the template, so the query is not made until then, after you have updated all the unread notifications.
The way to fix this is to explicitly evaluate the read queryset in the view, before you update the unread ones. You can do this by simply calling list on it:
context.update({'user_unread': user_unread, 'user_read': list(user_read)})
for msg in user_unread:
...
You can try getting another duplicate query set to update the objects. Use non-updated for template context and update only another one.
Like:
def get_context_data(self, **kwargs):
user = self.request.user
user_unread = user.notification_set.filter(viewed=False)
#Do some operation on qs so that it gets evaluated, like
nitems = len(user_unread)
#get another queryset to update
user_unread_toupdate = user.notification_set.filter(viewed=False)
user_read = user.notification_set.filter(viewed=True)
context = super(Notification, self).get_context_data(**kwargs)
context.update({'user_unread': user_unread, 'user_read': user_read})
#Use 2nd queryset
for msg in user_unread_toupdate:
msg.viewed = True
msg.save()
return context
Django would cache for each queryset differently. So once evaluated user_unread will have its own copy of objects.
Although, its not very elegant/efficient as multiple copies of similar queryset are loaded, so if number of records high, it will be slower.
How do I do this in a django template:
{% if li_status.profile_exists and (op1.conns|length > 0 or op2.conns|length > 0) %}
Parentheses are illegal in this expression and I'm not sure how the expression will be evaluated without them.
I prefer to do this in a single line and not using a nested if
As danihp said, there's often no reason to do such a thing in a template.
This kind of logic should be avoided in templates, that's why Django does not provide the ability to do that.
Did you try something like this in your view?
def your_view(request):
# Stuff
condition = li_status.profile_exists and (len(op1.conns) > 0 or len(op2.conns) > 0)
return render_to_response('your-template.html',
{
'condition': condition
},
RequestContext(request)
)
Then in your template:
{% if condition %}
...
If the condition is in a loop, and operates on properties of items within that queryset (as per Ben Davis' comment), then the technique is to run the loop and decorate it before you put the result in the context. Below is one of many ways to do this:
# in the view somewhere
qs = MyObject.objects.all()
decorated = []
for item in qs:
item.field_that_does_not_exist_on_the_model = expensive(item)
decorated.append(item)
.. then you put decorated in the context, not the qs and use t like any other field:
{% for item in decorated %}
{% if item.field_that_does_not_exist_on_the_model %}
..
{% endif %}
{% endfor %}
Another bonus of doing it this way that if it is slow as heck, it is possible to optimize it. You cannot optimize queryset access after the queryset has been handed over to the template.
I want to do the following:
A user sees all eventrecords he previously created on one page and can edit them.
The problem I have is that if he edits one or more of them and inputs an invalid choice no validation errors are shown. Instead, nothing happens (if I have if changed_events.is_valid() in the code) or I get "ValueError at /coding/assignment/3/
The EventRecord could not be changed because the data didn't validate." If the user puts in valid data, saving works just fine.
I would like to show validation errors on the page, the way it is already working when creating new entries.
Following my code:
The view (I'm not posting my whole view, as it's fairly complex and everything else is working fine. These are the parts responsible for what's not working):
##### Show already created events on the page
current_article = paginator.page(current_page).object_list[0]
EventFormSet = modelformset_factory(EventRecord, can_delete=True, exclude=('coder','article','url','last_updated'), extra=0)
event_queryset = EventRecord.objects.filter(article__id=current_article.id).filter(coder=request.user.id)
coded_events = EventFormSet(queryset=event_queryset, prefix="event_form")
elif 'save_changes' in request.POST:
formset = CodingFormSet(prefix="coding_form")
changed_events = EventFormSet(request.POST, prefix="event_form")
# if changed_events.is_valid():
instances = changed_events.save()
try:
history_record = ArticleHistory.objects.filter(article__id=paginator.page(current_page).object_list[0].id).filter(coder=request.user.id)[0]
history_record.last_updated = datetime.datetime.now()
history_record.save()
except:
history_form = ArticleHistoryForm()
article_history = history_form.save(commit=False)
article_history.article = paginator.page(current_page).object_list[0]
article_history.coder = request.user
article_history.last_updated = datetime.datetime.now()
article_history.save()
redirect_to = "?page=%s" % current_page
return HttpResponseRedirect(redirect_to)
The template:
{% for eventrecord in coded_events %}
{{ eventrecord.id }}
<div class="form-container">
{% if eventrecord.non_field_errors %}
{{form.non_field_errors}}
{%endif%}
{% for field in eventrecord %}
{% if field.errors %}
<li>{{ field.errors|striptags }}</li>
{% endif %}
{% endfor %}
Has anyone an idea what I'm doing wrong?
Clearly Django is recognizing that something is going wrong, but why doesn't it show it in the template but creates an error-page? Why does nothing happen when I include the is_valid()?
I really don't know what I'm supposed to do, any help is greatly appreciated!
Oh man, this was stupid:
Right after posting my question I saw the answer, after working days on it.
Here's what went wrong:
The queryset was passed to the template as coded_events.
During the save_changes operation, I renamed it to changed_events, which then couldn't be passed back to the template in case of validation errors.
After fixing that, everything works fine.